网站首页 > 博客文章 正文
翻译:叩丁狼教育吴嘉俊
背景
在上一篇文章中,我们介绍了逃逸分析,并且介绍了通过EA,JVM可以直接在栈上为未逃逸对象分配空间,而不需要在堆上分配空间。在文章发布之后,Caleb Cushing问了一个很有趣的问题:
如果一个逃逸对象被限定在调用者的范围之内,那么这个逃逸对象是否可以被EA优化?
我在这篇文章中给出了问题的答案。
一个例子
我们先创建一个如下的简单类:Person
public class Person { private final String firstName; private final String middleName; private final String lastName; public Person(String firstName, String middleName, String lastName) { this.firstName = requireNonNull(firstName); // Cannot be null this.middleName = middleName; // Can be null this.lastName = requireNonNull(lastName); // Cannot be null } public String getFirstName() { return firstName; } public Optional<String> getMiddleName() { return Optional.ofNullable(middleName); } public String getLastName() { return lastName; } }
假如我们调用Person::getMiddleName方法,很明显,Optional对象就是一个逃逸对象,因为它可以被任何调用这个方法的对象访问,所以返回的这个对象会被标记为全局逃逸对象,既然是逃逸对象,那么按理,应该会在堆里面为这个Optional对象分配空间。
但是,这真说不一定。JVM在某些情况下,确实可能把Optional对象直接在栈上分配,即使这个Optional对象会逃逸出getMiddleName方法。这可能么?
如何让一个全局逃逸对象仍然在栈上分配空间
事实在于,C2编译器在执行逃逸分析的时候,并不仅仅只是去分析某一个方法的调用,而是会在更大的内联代码块( chunks of code that is inlined)上去分析一个对象是否可以执行EA。内联是一种优化方案,代码会首先消除冗余调用,把代码执行“扁平化”操作,即多个层次的代码调用会被“扁平化”为一个指令序列。然后编译器在这个已经扁平化的代码块上再执行EA操作。所以,即使一个对象从一个方法中逃逸,如果在更大的内联代码块中没有逃逸,那也可以被优化。
下面给一个具体的例子来证明内联的逃逸对象是怎么被EA的
public class Main2 { public static void main(String[] args) throws IOException { Person p = new Person("Johan", "Sebastian", "Bach"); count(p); System.gc(); System.out.println("Press any key to continue"); System.in.read(); long sum = count(p); System.out.println(sum); System.out.println("Press any key to continue2"); System.in.read(); sum = count(p); System.out.println(sum); System.out.println("Press any key to exit"); System.in.read(); } private static long count(Person p) { long count = 0; for (int i = 0; i < 1_000_000; i++) { if (p.getMiddleName().isPresent()) { count++; } } return count; } }
上面的代码创建了一个Person对象,并且大量的调用了Person对象的getMiddleName()方法。我们分成三步来测试。第一步在调用完count方法之后,立刻进行GC操作回收我们创建的对象。另外两步不会从堆里面回收任何数据,我们来看看每个步骤在堆上数据的区别。我们按照下面的参数运行代码:
-server -XX:BCEATraceLevel=3 -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -verbose:gc -XX:MaxInlineSize=256 -XX:FreqInlineSize=1024 -XX:MaxBCEAEstimateSize=1024 -XX:MaxInlineLevel=22 -XX:CompileThreshold=10 -Xmx4g -Xms4g
在执行完第一步(GC之后),内存情况如下:
pemi$ jps | grep Main2 74886 Main2 num #instances #bytes class name ---------------------------------------------- 1: 95 42952184 [I 2: 1062 101408 [C 3: 486 55384 java.lang.Class 4: 526 25944 [Ljava.lang.Object; 5: 13 25664 [B 6: 1040 24960 java.lang.String 7: 74 5328 java.lang.reflect.Field
后面两步内存情况分别如下:
pemi$ jmap -histo 74886 | head num #instances #bytes class name ---------------------------------------------- 1: 95 39019792 [I 2: 245760 3932160 java.util.Optional 3: 1063 101440 [C 4: 486 55384 java.lang.Class 5: 526 25944 [Ljava.lang.Object; 6: 13 25664 [B 7: 1041 24984 java.lang.String pemi$ jmap -histo 74886 | head num #instances #bytes class name ---------------------------------------------- 1: 95 39019544 [I 2: 245760 3932160 java.util.Optional 3: 1064 101472 [C 4: 486 55384 java.lang.Class 5: 526 25944 [Ljava.lang.Object; 6: 13 25664 [B 7: 1042 25008 java.lang.String
可以明显的看到,在第二步和第三步之间,没有新的Optionals对象被创建,EA确实没有在堆上创建Optinal对象,即使它们从创建和返回它们的初始方法中逃逸。因为这个特性,所以,我们仍然能在代码级别做适当的抽象,而不对性能产生影响。
原文:https://www.voxxed.com/2016/01/java-8-jvm-can-re-capture-objects-escaped/
猜你喜欢
- 2024-09-18 3分钟搞清楚 JVM逃逸分析(java 逃逸对象)
- 2024-09-18 天天都是面对对象编程,你真的了解你的对象吗?
- 2024-09-18 做JAVA开发的同学一定遇到过的爆表问题,看这里解决
- 2024-09-18 线上一次fullgc搞得鸡飞狗跳后,我总结了这篇文章
- 2024-09-18 Java中的对象都是在堆上分配的吗?
- 2024-09-18 jvm 相关的线上问题,内存使用率飙升到 90%+ 等 处理手段
- 2024-09-18 面试官问我JVM问题,我直接回怼他
- 2024-09-18 JVM入门第2部分-调试内存问题(jvm内存调优方法)
- 2024-09-18 互联网大厂面试系列-面试被问到什么是JVM的逃逸分析?
- 2024-09-18 内存溢出OutOfMemoryError科普系列一
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)