逃逸分析:分析变量是否逃出它的作用域
- 全局变量赋值逃逸
- 方法返回值逃逸
- 实例引用逃逸
- 线程逃逸:赋值给类变量或者可以在其他线程中访问的实例变量
public class EscapeTest {
public static SomeClass someClass;
// 1
public void globalVariablePointerEscape(){
someClass = new SomeClass();
}
// 2
public SomeClass methodPointerEscape(){
return new SomeClass();
}
// 3 this原本作用于当前对象,通过参数逃逸到SomeClass对象中
public void instancePassPointerEscape(){
this.methodPointerEscape().printClassTime(this);
}
}
class SomeClass {
public void printClassTime(EscapeTest escapeTest) {
System.out.println(escapeTest.getClass().getName());
}
}
逃逸状态标记
- 全局级别逃逸:一个对象可能从方法或者当前线程中逃逸
- 对象被作为方法的返回值
- 对象作为静态字段( static field)或者成员变量(field)
- 如果重写了某个类的 finalize()方法,那么这个类的对象都会被标记为全局逃逸状态并且一定会放在堆内存中。
- 参数级别逃逸
- 对象被作为参数传递给一个方法,但是在这个方法之外无法访问/对其他线程不可见。
- 无逃逸:一个对象不会逃逸。
标量替换
标量是指不能进一步分解的变量,包括基础数据类型、对象引用。聚合量是指可以进一步分解的量。
通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM不会创建该对象,而是创建它的成员变量来代替。-XX:+EliminateAllocations
开启标量替换(JDK8默认开启)
public void someTest(){
SomeTest someTest = new SomeTest();
someTest.id = 1;
someTest.age = 2;
// 开启标量替换后,不会再创建对象,而是转换为变量
int id = 1;
int age = 2;
}
栈上分配
通过逃逸分析,能够确认对象不会被外部访问,就会在栈上分配对象。对象占用的空间在方法结束栈帧出栈时销毁,减轻GC压力。