为什么 jvm -XX:+EliminateAllocations 失败

why jvm -XX:+EliminateAllocations fail

OnStackTest.java

public class OnStackTest {

    public static void alloc() {
        User u = new User();
        u.id = 5;
        u.name = "test";
    }
    public static void main(String[] args) throws InterruptedException {
        long b = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            Thread.sleep(50);
            alloc();
        }
        long e = System.currentTimeMillis();
        System.out.println(e - b);
    }
}

User.java

public class User {
    public int id = 0;

    public String name = "";

    public User() {
    }
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

JVM 标志 -server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations

使用jmap -histo

发现一直在堆上创建了user对象。理论上,我们不应该用标量替换用户对象,也不要在堆上创建对象?

DoEscapeAnalysisEliminateAllocations 标志默认启用 - 无需明确设置它们。

EliminateAllocations标志是C2编译器特有的,声明在c2_globals.hpp中。但是在你的测试中,这个方法很长时间都没有被 C2 编译过。添加 -XX:+PrintCompilation 标志以确保:

    ...
   1045   84       3       java.lang.StringBuffer::<init> (6 bytes)
   1045   85  s    3       java.lang.StringBuffer::toString (36 bytes)
   1045   86       3       java.util.Arrays::copyOf (19 bytes)
  15666   87     n 0       java.lang.Thread::sleep (native)   (static)
  15714   88       3       OnStackTest::alloc (20 bytes)
 311503   89       4       OnStackTest::alloc (20 bytes)
 311505   88       3       OnStackTest::alloc (20 bytes)   made not entrant

这表明alloc在15秒后由C1(第3层)编译。一个方法需要调用数千次才能被 C2 考虑为 re-compilation。鉴于迭代之间有 50 毫秒的延迟,这不会很快发生。在我的实验中,alloc 仅在 运行.

5 分钟后才被 C2 编译

C2 编译方法不再包含分配。
我用 -XX:CompileCommand="print,OnStackTest::alloc"

验证了这一点
  # {method} {0x0000000012de2bc0} 'alloc' '()V' in 'OnStackTest'
  #           [sp+0x20]  (sp of caller)
  0x0000000003359fc0: sub     rsp,18h
  0x0000000003359fc7: mov     qword ptr [rsp+10h],rbp  ;*synchronization entry
                                                ; - OnStackTest::alloc@-1 (line 4)

  0x0000000003359fcc: add     rsp,10h
  0x0000000003359fd0: pop     rbp
  0x0000000003359fd1: test    dword ptr [0df0000h],eax
                                                ;   {poll_return}
  0x0000000003359fd7: ret

顺便说一句,我建议使用 JMH for such kind of tests. Otherwise it's too easy to fall into one of common benchmarking pitfalls. Here is a ,它也试图衡量分配消除的效果,但它错了。