为什么getSum没有被热点jvm内联?

时间:2018-03-11 07:32:11

标签: java jit jvm-hotspot escape-analysis

以下是我尝试从<{>逃生分析主题的Java Performance: The Definitive Guide, Page 97重现的示例。这可能是应该发生的事情:

  1. getSum()必须足够热,并且必须使用适当的JVM参数将其内联到调用者main()
  2. 由于listsum变量都没有从main()方法中逃脱,因此它们可以标记为NoEscape,因此JVM可以使用堆栈分配而不是堆 - 分配
  3. 但是我通过jitwatch运行它,结果显示getSum()编译成本机程序集,而不是内联到main()。更不用说堆栈分配也没有发生。

    我在这里做错了什么? (我已经把整个代码和热点日志here。)

    以下是代码:

    import java.math.BigInteger;
    import java.util.ArrayList;
    import java.util.stream.IntStream;
    
    public class EscapeAnalysisTest {
    
        private static class Sum {
    
            private BigInteger sum;
            private int n;
    
            Sum(int n) {
                this.n = n;
            }
    
            synchronized final BigInteger getSum() {
                if (sum == null) {
                    sum = BigInteger.ZERO;
                    for (int i = 0; i < n; i++) {
                        sum = sum.add(BigInteger.valueOf(i));
                    }
                }
                return sum;
            }
    
        }
    
        public static void main(String[] args) {
            ArrayList<BigInteger> list = new ArrayList<>();
            for (int i = 1; i < 1000; i++) {
                Sum sum = new Sum(i);
                list.add(sum.getSum());
            }
            System.out.println(list.get(list.size() - 1));
        }
    
    }
    

    我使用的JVM参数:

    -server
    -verbose:gc
    -XX:+UnlockDiagnosticVMOptions
    -XX:+TraceClassLoading
    -XX:MaxInlineSize=60
    -XX:+PrintAssembly
    -XX:+LogCompilation
    

1 个答案:

答案 0 :(得分:4)

为了了解内联内容的原因,您可以在编译日志中查找inline_successinline_fail标记。

然而,即使得到内联的内容,也必须编译调用者,在你的情况下你需要main方法中的内联,所以这将发生的唯一方法是栈上替换(OSR) 。查看您的日志,您可以看到一些OSR编译,但没有main方法:您的main方法中的工作量不足。

您可以通过增加for循环的迭代次数来解决这个问题。通过将其增加到100_000,我得到了第一个OSR编译。

对于这样一个小例子,我看了-XX:+PrintCompilation -XX:+PrintInlining而不是整个LogCompilation输出,我看到了:

@ 27   EscapeAnalysisTest$Sum::getSum (51 bytes)   inlining prohibited by policy

这不是很有用......但是看一点HotSpot源代码就会发现,这可能是因为一个策略阻止了C1编译内联方法,这些方法已经被C2编译过OSR。 在任何情况下,查看C1编译完成的内联都不是那么有趣。

1_000_000的参数上添加更多循环迭代(Sum并使用模数来减少运行时间)使我们获得main的C2 OSR:

@31   EscapeAnalysisTest$Sum::getSum (51 bytes)   already compiled into a big method  

C2的策略部分是自我描述性的,由InlineSmallCode标志控制:-XX:InlineSmallCode=4k告诉HotSpot“大方法”的阈值是4kB的本机代码。在我的机器上足以让getSum内联:

  14206   45 %     4       EscapeAnalysisTest::main @ 10 (61 bytes)
                              @ 25   EscapeAnalysisTest$Sum::<init> (10 bytes)   inline (hot)
                                @ 1   java.lang.Object::<init> (1 bytes)   inline (hot)
              s               @ 31   EscapeAnalysisTest$Sum::getSum (51 bytes)   inline (hot)
                                @ 31   java.math.BigInteger::valueOf (62 bytes)   inline (hot)
                                  @ 58   java.math.BigInteger::<init> (77 bytes)   inline (hot)
                                    @ 1   java.lang.Number::<init> (5 bytes)   inline (hot)
                                      @ 1   java.lang.Object::<init> (1 bytes)   inline (hot)
                                @ 34   java.math.BigInteger::add (123 bytes)   inline (hot)
                                  @ 41   java.math.BigInteger::add (215 bytes)   inline (hot)
                                  @ 48   java.math.BigInteger::<init> (38 bytes)   inline (hot)
                                    @ 1   java.lang.Number::<init> (5 bytes)   inline (hot)
                                      @ 1   java.lang.Object::<init> (1 bytes)   inline (hot)
                              @ 34   java.util.ArrayList::add (29 bytes)   inline (hot)
                                @ 7   java.util.ArrayList::ensureCapacityInternal (13 bytes)   inline (hot)
                                  @ 6   java.util.ArrayList::calculateCapacity (16 bytes)   inline (hot)
                                  @ 9   java.util.ArrayList::ensureExplicitCapacity (26 bytes)   inline (hot)
                                    @ 22   java.util.ArrayList::grow (45 bytes)   too big

(请注意,我从未使用MaxInlineSize

这里是参考修改后的循环:

for (int i = 1; i < 1_000_000; i++) {
  Sum sum = new Sum(i % 10_000);
  list.add(sum.getSum());
}