这篇文章主要介绍“JVM内存增强之逃逸分析怎么使用”,在日常操作中,相信很多人在JVM内存增强之逃逸分析怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JVM内存增强之逃逸分析怎么使用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
逃逸分析一种数据分析算法,基于此算法可以有效减少 Java 对象在堆内存中的分配。 Hotspot 虚拟机的编译器能够分析出一个新对象的引用范围,然后决定是否要将这个对象分配到堆上.
当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。
当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。
//对象发生了逃逸,不会在栈上分配,有可能导致GC STW public StringBuffer append(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb; } //对象未发生逃逸 public String append(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb.toString(); }
建议:开发中能在方法内部应用对象的,就尽量控制在内部
在 JDK 1.7 版本之后, HotSpot 中默认就已经开启了逃逸分析,如果使用的是较早的
版本,开发人员则可以通过:
✓ 选项“ -XX:+DoEscapeAnalysis" 显式开启逃逸分析。
✓ 通过选项“ -XX:+PrintEscapeAnalysis" 查看逃逸分析的筛选结果。
编译器可以对代码做如下优化
1.栈上分配:将堆分配转化为栈分配。如果一个对象在方法内创建,要使指向该对象的引用不会发生逃逸,对象可能是栈上分配的候选
/** * 栈上分配测试(-XX:+DoEscapeAnalysis) * -Xmx128m -Xms128m -XX:+DoEscapeAnalysis -XX:+PrintGC */ public class ObjectStackAllocationTests { public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { alloc(); } long end = System.currentTimeMillis(); System.out.println("花费的时间为: " + (end - start) + " ms"); // 为了方便查看堆内存中对象个数,线程 sleep TimeUnit.MINUTES.sleep(5); } private static void alloc() { byte[] data = new byte[10];//未发生逃逸 } }
2.同步锁消除:
我们知道线程同步是靠牺牲性能来保证数据的正确性,这个过程的代价会非常高。程序 的并发行和性能都会降低。JVM 的 JIT 编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程应用?假如是,那么 JIT 编译器在编译这个同步块的时候就会取消对这部分代码上加的锁。这个取消同步的过程就叫同步省略,也叫锁消除
public class SynchronizedLockTest { public void lock() { Object obj= new Object(); synchronized(obj) { System.out.println(obj); } }
3.标量替换分析
所谓的标量(scalar)一般指的是一个无法再分解成更小数据的数据。例如,Java 中 的原始数据类型就是标量。相对的,那些还可以分解的数据叫做聚合量(Aggregate),Java 中的对象就是聚合量,因为他可以分解成其他聚合量和标量。在 JIT 阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过 JIT 优化,就会把这个对象分解成若干个变量来代替。这个过程就是标量替换。
public class ObjectScalarReplaceTests { public static void main(String args[]) { long start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { alloc(); } long end = System.currentTimeMillis(); System.out.println("花费的时间为: " + (end - start) + " ms"); } private static void alloc() { Point point = new Point(1,2); } static class Point { private int x; private int y; public Point(int x,int y){ this.x=x; this.y=y; } } //对于上面代码,假如开启了标量替换,那么 alloc 方法的内容就会变为如下形式 private static void alloc() { int x=10; int y=20; }
alloc 方法内部的 Point 对象是一个聚合量,这个聚合量经过逃逸分析后,发现他并没有逃逸,就被替换成两个标量了。那么标量替换有什么好处呢?可以大大减少堆内存的占用。因为一旦不需要创建对象了,那么就不再需要分配堆内存了。标量替换为栈上分配 提供了很好的基础。
1.什么是逃逸分析?
可以有效减少 Java 对象在堆内存中的分配压力和同步负载的算法
2.逃逸分析有什么优势、劣势?
逃逸分析是需要消耗一定的性能去执行分析的,所以说如果方法中的对象全都是处于逃逸状态,那么就没有起到优化的作用,从而就白白损失了这部分的性能消耗
到此,关于“JVM内存增强之逃逸分析怎么使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。