逃逸分析
逃逸分析只有在server模式下才可以开启逃逸分析,可以通过"-server"启动server模式
C:\Users\admin>java -version
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
逃逸分析(Escape Analysis)是目前Java虚拟机中比较前沿的优化技术,它与类型继承关系分析一样,并不是直接优化代码的手段,而是为其他优化措施提供依据的分析技术
逃逸分析的基本原理是:分析对象动态作用域,当一个对象在方法里面被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,这种称为方法逃逸;甚至还有可能被外部线程访问到,譬如赋值给可以在其他线程中访问的实例变量,这种称为线程逃逸;从不逃逸、方法逃逸到线程逃逸,称为对象由低到高的不同逃逸程度。
会发送逃逸的场景
// 快速判断一个对象是否发送了逃逸,看这个对象的实例是否可能在方法外被调用
// 1、作为方法的返回
public Data getDate(){
return new Data();
}
// 2、对于成员变量的赋值操作
public Data data = null;
public void getDate(){
data = data == null ? new Data() : data;
}
// 3、引用了成员变量的值
public Data data = new Data();
public void useData(){
Data d = data;
}
在JDK 6u23(或者直接说在JDK7及以后的) 版本中,默认开启了逃逸分析。如果使用的是早期的版本,可以使用“-XX:+DoEscapeAnalysis”显式的开启逃逸分析。通过“-XX:+PrintEscapeAnalysis”查看逃逸分析的筛选结果
如果发生了逃逸,就可能将对象分配在栈上(栈上分配)
栈上分配
如果确定一个对象不会逃逸出线程之外,栈上分配可以支持方法逃逸,但不能支持线程逃逸。
例子:
没有开启逃逸分析
参数:-Xms3G -Xmx3G -XX:+PrintGCDetails -XX:-DoEscapeAnalysis
开启了逃逸分析
参数:-Xms3G -Xmx3G -XX:+PrintGCDetails
备注:如果对象是在栈上分配,那么对象会随着方法的出栈消失。栈是没有垃圾回收的,只有入栈和出栈。
同步消除
同步消除(Synchronization Elimination):线程同步本身是一个相对耗时的过程,如果逃逸分析能够确定一个变量不会逃逸出线程,无法被其他线程访问,那么这个变量的读写肯定就不会有竞争,对这个变量实施的同步措施也就可以安全地消除掉。这种情况针对的是synchronized锁,而对于Lock锁,则JVM并不能消除。
例子:
// 我们不从JVM的角度也知道,这个锁是没用的,还影响效率,可以去除
// 加了jvm也会去除
Data data = new Data();
synchronized (data){
data.setId(1);
}
备注:逃逸分析是在java文件加载成字节码文件之后才进行的,所以在字节码文件中仍然可看的锁相关的内容
标量替换
标量:无法再分解成更小数据的数据,基本数据类型。
聚合量:非标量,引用数据类型,比如对象
在JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问,并且这个对象可以被拆散,那么程序真正执行的时候将可能不去创建这个对象,而改为直接创建它的若干个被这个方法使用的成员变量来代替。将对象拆分后,可以让对象的成员变量在栈上分配的和读写,标量替换可以视作栈上分配的一种特例,实现更简单(不用考虑整个对象完整结构的分配),但对逃逸程度的要求更高,它不允许对象逃逸出方法范围内。
Data data = new Data();
data.setId(1);
data.setContent("2");
// ||
// ||
// \/
int id = 1;
String content = "2";
标量替换默认开启:也可以使用参数"-XX:+EliminateAllocations"来显示的开启和关闭
例子
开启
参数:
关闭
参数: -Xms256M -Xmx256M -XX:+PrintGC -XX:-DoEscapeAnalysis -XX:-EliminateAllocations
发现:开启了历时短,而且没有GC。如果 -XX:-DoEscapeAnalysis 但是 -XX:+EliminateAllocations也不会开启
总结
逃逸分析还不成熟,直到JDK 6,HotSpot才开始支持初步的逃逸分析,而且到现在这项优化技术尚未足够成熟,仍有很大的改进余地。不成熟的原因主要是逃逸分析的计算成本非常高。所以目前虚拟机只能采用不那么准确,但时间压力相对较小的算法来完成分析。
以上的例子,实际上都是标量替换,把对象打散存储在局部变量表中。