首页 > 解决方案 > 为什么 jvm -XX:+EliminateAllocations 失败

问题描述

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);
    }
}

用户.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

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

标签: javajvmjit

解决方案


DoEscapeAnalysis默认情况下启用和EliminateAllocations标志 - 无需显式设置它们。

EliminateAllocationsflag 特定于 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 考虑重新编译。考虑到迭代之间有 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进行此类测试。否则很容易陷入常见的基准测试陷阱之一。这是一个类似的问题,它也试图衡量分配消除的效果,但做错了。


推荐阅读