了解-XX:+ PrintCompilation的输出

我在Java列表迭代代码上运行一些微基准测试。 我使用了-XX:+ PrintCompilation和-verbose:gc标志来确保在运行定时时后台没有发生任何事情。 但是,我在输出中看到了一些我无法理解的东西。

这是代码,我正在运行基准测试:

import java.util.ArrayList; import java.util.List; public class PerformantIteration { private static int theSum = 0; public static void main(String[] args) { System.out.println("Starting microbenchmark on iterating over collections with a call to size() in each iteration"); List nums = new ArrayList(); for(int i=0; i<50000; i++) { nums.add(i); } System.out.println("Warming up ..."); //warmup... make sure all JIT comliling is done before the actual benchmarking starts for(int i=0; i<10; i++) { iterateWithConstantSize(nums); iterateWithDynamicSize(nums); } //actual System.out.println("Starting the actual test"); long constantSizeBenchmark = iterateWithConstantSize(nums); long dynamicSizeBenchmark = iterateWithDynamicSize(nums); System.out.println("Test completed... printing results"); System.out.println("constantSizeBenchmark : " + constantSizeBenchmark); System.out.println("dynamicSizeBenchmark : " + dynamicSizeBenchmark); System.out.println("dynamicSizeBenchmark/constantSizeBenchmark : " + ((double)dynamicSizeBenchmark/(double)constantSizeBenchmark)); } private static long iterateWithDynamicSize(List nums) { int sum=0; long start = System.nanoTime(); for(int i=0; i<nums.size(); i++) { // appear to do something useful sum += nums.get(i); } long end = System.nanoTime(); setSum(sum); return end-start; } private static long iterateWithConstantSize(List nums) { int count = nums.size(); int sum=0; long start = System.nanoTime(); for(int i=0; i<count; i++) { // appear to do something useful sum += nums.get(i); } long end = System.nanoTime(); setSum(sum); return end-start; } // invocations to this method simply exist to fool the VM into thinking that we are doing something useful in the loop private static void setSum(int sum) { theSum = sum; } } 

这是输出。

  152 1 java.lang.String::charAt (33 bytes) 160 2 java.lang.String::indexOf (151 bytes) 165 3Starting microbenchmark on iterating over collections with a call to size() in each iteration java.lang.String::hashCode (60 bytes) 171 4 sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (490 bytes) 183 5 java.lang.String::lastIndexOf (156 bytes) 197 6 java.io.UnixFileSystem::normalize (75 bytes) 200 7 java.lang.Object:: (1 bytes) 205 8 java.lang.Number:: (5 bytes) 206 9 java.lang.Integer:: (10 bytes) 211 10 java.util.ArrayList::add (29 bytes) 211 11 java.util.ArrayList::ensureCapacity (58 bytes) 217 12 java.lang.Integer::valueOf (35 bytes) 221 1% performance.api.PerformantIteration::main @ 21 (173 bytes) Warming up ... 252 13 java.util.ArrayList::get (11 bytes) 252 14 java.util.ArrayList::rangeCheck (22 bytes) 253 15 java.util.ArrayList::elementData (7 bytes) 260 2% performance.api.PerformantIteration::iterateWithConstantSize @ 19 (59 bytes) 268 3% performance.api.PerformantIteration::iterateWithDynamicSize @ 12 (57 bytes) 272 16 performance.api.PerformantIteration::iterateWithConstantSize (59 bytes) 278 17 performance.api.PerformantIteration::iterateWithDynamicSize (57 bytes) Starting the actual test Test completed... printing results constantSizeBenchmark : 301688 dynamicSizeBenchmark : 782602 dynamicSizeBenchmark/constantSizeBenchmark : 2.5940773249184588 

我不理解输出中的这四行。

 260 2% performance.api.PerformantIteration::iterateWithConstantSize @ 19 (59 bytes) 268 3% performance.api.PerformantIteration::iterateWithDynamicSize @ 12 (57 bytes) 272 16 performance.api.PerformantIteration::iterateWithConstantSize (59 bytes) 278 17 performance.api.PerformantIteration::iterateWithDynamicSize (57 bytes) 

  • 为什么这两种方法都被编译两次?
  • 我如何阅读此输出…各种数字的含义是什么?

我将尝试在Thomas Jungblut发布的链接的帮助下回答我自己的问题。

 260 2% performance.api.PerformantIteration::iterateWithConstantSize @ 19 (59 bytes) 268 3% performance.api.PerformantIteration::iterateWithDynamicSize @ 12 (57 bytes) 272 16 performance.api.PerformantIteration::iterateWithConstantSize (59 bytes) 278 17 performance.api.PerformantIteration::iterateWithDynamicSize (57 bytes) 

第一栏

第一列’260’是时间戳。

第二栏

第二列是compilation_id和method_attributes。 触发HotSpot编译时,每个编译单元都会获得编译ID。 第二列中的数字是编译ID。 JIT编译和OSR编译有两个不同的编译ID序列。 所以1%和1是不同的编译单元。 前两行中的%表示这是OSR编译的事实。 由于代码在大循环上循环,并且VM确定此代码很热,因此触发了OSR编译。 因此,触发了OSR编译,这将使VM能够执行On Stack Replacement并在准备就绪后转移到优化代码。

第三栏

第三列performance.api.PerformantIteration::iterateWithConstantSize是方法名称。

第四栏

当OSR编译发生时,第四列也是不同的。 我们先来看看常见的部分。 第四列的末尾(59字节),指的是字节码中编译单元的大小(不是编译代码的大小)。 OSR编译中的@ 19部分是指osr_bci。 我将引用上面提到的链接 –

Java方法中的“位置”由其字节码索引(BCI)定义,触发OSR编译的位置称为“osr_bci”。 OSR编译的nmethod只能从其osr_bci中输入; 只要它们的osr_bci不同,就可以同时存在同一方法的多个OSR编译版本。

最后,为什么方法编译两次?

第一个是OSR编译,可能是在循环运行时由于预热代码(在示例中)而发生,第二个编译是JIT编译,可能是为了进一步优化编译代码?

我认为第一次OSR发生了,然后它改变了调用计数器跳转方法compilar(PS:抱歉,我的英语是池)