【问题标题】:Empty loop consuming more memory than non empty loop in javajava中的空循环比非空循环消耗更多的内存
【发布时间】:2014-08-20 10:57:52
【问题描述】:

我正在阅读有关 java 性能调整的内容并遇到了这个问题。

当我们运行时

public class test {
public static void main(String a[]){
    for(int i=0;i<1000000;i++){
        for(int j=0;j<100000;j++){
            Double d = new Double(1.0);
        }
    }
}
}

JVisualVM 显示了一个平坦的内存消耗图:

但是当我们运行下面的代码时,

public class test {
public static void main(String a[]){
    for(int i=0;i<1000000;i++){
        for(int j=0;j<100000;j++){
        }
    }
}
}

JVisualVM 呈现锯齿:

为什么会这样? 两种情况下如何以及为何更改 gc 触发限制?

【问题讨论】:

  • 我认为空循环需要更多的处理器。所以 GC 没有机会运行。
  • Java 的哪个版本?什么参数?空循环的第二次测试应该工作 0 秒,因为 java 运行空循环并不是那么愚蠢。
  • @Andremoniy - 没有。java 可以运行空循环(即使在引入逃逸分析之后,即在 jdk6U23 之后)
  • 我必须说,这是一个很好的问题,用一个简单的例子证明在进行性能测试时不做任何假设是多么重要。我们可以得到你正在阅读这篇文章的来源吗?是 Oracle 性能调优文章还是一本书?

标签: java memory-management jvm


【解决方案1】:

关于你的v1 for loops,你的局部变量,一旦它退出了它的作用域,它就会被标记为可以免费收集的GC,所以每隔一段时间,GC 就会启动并收集该内存。

对于您的v2 for loops,那些空循环不会被编译器“优化掉”**,只有在多次调用之后,因为

在某段代码执行了很多次之后,JIT 触发 次 [1]

关于你的锯齿图案,ruakh 有一个很好的解释[2]:

如果我没记错的话,部分原因是监视器本身强制应用程序创建临时对象,其中包含有关垃圾收集状态和内存使用情况的信息。

** 它们可能永远不会被删除,因为众所周知,这些空循环被用作等待机制。*

[1]Java: how much time does an empty loop use? - Simone Gianni's answer

[2]Why does an empty Java program consume memory?

【讨论】:

    【解决方案2】:

    原因是代码编译后的优化算法。在第一种情况下,因为您每次都创建一个 double 而没有记录它。这会使程序不断使用 GC。因此编译器会优化代码,从而减少使用的内存。空循环是一种特殊情况,因为许多程序员使用它来使线程等待。所以编译器不会尝试优化它。

    【讨论】:

    • 这不是真的。 HotSpot JIT 很乐意尽可能优化空循环。 C2 编译器(又名“服务器 VM”)可以并且可以做到。第二个测试用例在 x64 JDK 上立即退出。但似乎 OP 正在 C1(又名“客户端 VM”)上运行测试——这个编译器不够聪明,无法删除一个空循环。
    • @apangin 有许多不同的编译器。这是“javac”使用的优化之一。事实上,它也用于优化空循环,但是由于游戏行业的一些要求,他们将其改回。 (我不知道为什么,但显然有些游戏制作者使用空循环等待)
    猜你喜欢
    • 2014-09-23
    • 2021-08-09
    • 2015-01-16
    • 2011-12-15
    • 2016-07-11
    • 1970-01-01
    • 2018-03-01
    • 2018-01-08
    相关资源
    最近更新 更多