java - 为什么 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
发现用户对象一直在堆上创建。理论上,我们不应该用标量替换用户对象,也不应该在堆上创建对象?
解决方案
DoEscapeAnalysis
默认情况下启用和EliminateAllocations
标志 - 无需显式设置它们。
EliminateAllocations
flag 特定于 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进行此类测试。否则很容易陷入常见的基准测试陷阱之一。这是一个类似的问题,它也试图衡量分配消除的效果,但做错了。
推荐阅读
- algorithm - 查找图中的边数的算法,其中顶点由集合定义
- python - 通过运行 open 或其他任何方式创建文件,我们可以用密码保护它吗
- telegram - 有没有办法在电报组描述文本中添加超链接?
- c# - 使用 AMQP 协议时,如何在 Azure 中识别从设备到 IoT 中心的消息大小?
- tiddlywiki - 当您将图像嵌入时,如何避免 Stroll 或 TiddlyWiki 将图像列为孤儿?
- .net - 使用 dotnet-trace 获取内存时为空 Speedscope 文件
- graphql - 如何在 graphQl/Sequelize 中的嵌套查询上应用 where 子句?
- influxdb - 将来自两个不同查询的两个值相除
- javascript - Node JS 和 express 公用文件夹 404 错误它查看错误的文件名
- c# - 预制件未在 Unity 中选择特定对象预制件