一、为什么“逃逸”
逃逸分析是指分析指针动态范围 的方法:同编译器优化原理的指针分析和外形分析相关联。当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,就会被其他方法或者线程所引用,称逃逸。 通俗讲,一个对象指针被多个方法或者线程引用时
public StringBuilder escapeDemo1(String a, String b) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(a);
stringBuilder.append(b);
return stringBuilder;
}
stringBuilder是方法内部变量被直接返回,stringBuilder可能被其他地方的方法或参数所改变,作用域不只是demo1,虽是局部变量,但“逃逸”。
public String escapeDemo2(String a, String b) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(a);
stringBuilder.append(b);
return stringBuilder.toString();//没有发生逃逸。
}
二、什么是逃逸分析
目的:有效减少Java 程序中 同步负载和内存堆分配压力 的跨函数全局数据流分析算法。
Java Hotspot编译器分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。前沿优化技术了。
三、逃逸分析的原理
Java本身的限制(对象只能分配到堆中),为了减少临时对象在堆内分配的数量,方法体内定义局部变量,该变量在方法执行过程中未发生逃逸,
按照JVM调优机制,1)先堆内存创建,2)将对象引用压入调用栈,3)继续执行。
用逃逸分析对JVM进行优化:栈重新分配方式,1)先找未逃逸变量,2)将变量直接存到栈里,无需进入堆,3)分配完,继续调用栈内执行,最后线程执行结束,栈空间被回收,局部变量也被回收了。优化前在堆中,优化后在栈中
四、逃逸的方式
方法逃逸:对象跳出了方法(上面代码)
线程逃逸:对象被其他线程访问,赋值给实例变量,并被其他线程访问。对象逃出当前线程。
五、逃逸分析的好处
如果一个对象不会在方法体内,或线程内发生逃逸(或者说是通过逃逸分析后,使其未能发生逃逸)
1. 栈上分配
不逃逸的对象所占空间大,用栈空间,大量的对象将随方法的结束而销毁,减轻了GC压力
2. 同步消除
类的方法上有同步锁,运行时,只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行。
3. 标量替换
标量:不可分解原始数据类型(int,long等数值类型以及reference类型等)
聚合量:可分解数据(对象)
如果逃逸分析证明一个可分解对象不会被外部访问,那程序真正执行的时候将可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来代替。拆散后的变量便可以被单独分析与优化,可以各自分别在栈帧或寄存器上分配空间,原本的对象就无需整体分配空间。
六、开启设置
在JDK 6u23以上是默认开启,这里将设置重新明确一下:
强制开启:1-server -XX:+DoEscapeAnalysis -XX:+PrintGCDetail -Xmx10m -Xms10m
关闭逃逸分析:1-server -XX:-DoEscapeAnalysis -XX:+PrintGCDetail -Xmx10m -Xms10m
写在结尾
栈空间一般小,无法存储大容量数据。目前的实现都是采用不那么准确但是时间压力相对较小的算法来完成逃逸分析,可能导致效果不稳定。逃逸分析的效果只能在满足高频和高数量的小容量的变量分配结构,才是合适的。