Java:重复调用函数会导致性能提升的原因是什么?

use*_*127 5 java performance instrumentation garbage-collection jvm-hotspot

当我遇到一个奇怪的结果时,我正在做一些关于对象分配的性能测试.我有以下java代码:

public static long TestMethod(){
    int len = 10000000;
    Object[] obs = new Object[len];
    long t = System.nanoTime();
    for (int i = 0; i < len; i++) {
        obs[i] = new Object();
    }
    return System.nanoTime() - t;
}

public static void main(String... args) throws InterruptedException {
    for(int i = 0; i < 10; i++){
        System.gc();
        System.gc();//Wait for the gc to be finished
        Thread.sleep(1000);
        System.out.println(TestMethod());
    }
}
Run Code Online (Sandbox Code Playgroud)

期望:由于请求操作系统和热点增强的更大内存空间,第一次调用将比第二次调用慢.但第二和第三将几乎相同.

观察结果:

11284734000
799837000
682304000
304736000
380770000
392786000
374279000
381611000
379174000
407256000
Run Code Online (Sandbox Code Playgroud)

在第三次和第四次迭代之间仍然有相当大的加速.是什么导致这种加速?在测试其他功能时,如何确保测量结果准确无误,在测量之前是否必须调用该功能四次以上?

Ste*_*n C 4

是什么导致了这种加速?

它很可能是 JIT 编译,但也可能是代码加载和/或堆预热效果的贡献。

在测试其他函数时,如何确保我的测量结果准确?在测量之前是否必须调用该函数四次以上?

你需要做类似的事情。没有其他方法可以消除测量中的 JVM 预热影响,并且仍然获得有代表性的结果。为 Java 编写有效的“微基准”很困难,在尝试之前您需要阅读所有问题。从这个开始:如何用 Java 编写正确的微基准?


我还要指出其他一些事情:

  1. 您尝试从测量中消除垃圾收集的成本(我认为您似乎正在这样做)似乎失败了。看起来您在执行期间收到了少量集合testMethod。这将解释“稳态”结果大约 7% 的变异性。

  2. 将分配对象的成本与释放对象的成本分开可能会给您带来误导性的结果。分配对象的“总”成本包括回收内存时将内存清零的成本……这是由垃圾收集器完成的。

  3. 事实上,最有用的衡量标准是分配/收集周期中每个对象的摊余成本。(令人惊讶的是)这取决于 GC 运行时的非垃圾量……这是您的基准测试没有考虑到的。