逃逸分析是Java HotSpot Server编译器中JIT优化的一个重要步骤。它在Java SE 6u23及以后的版本中默认启用。
对象基于逃逸分析可以有三种状态:全局逃逸(GlobalEscape)、参数逃逸(ArgEscape)和无逃逸(NoEscape)。
public class GlobalEscapeExample {
private static Object staticObject;
public void globalEscape() {
staticObject = new Object(); // 这个对象赋值给静态字段,因此它是全局逃逸的
}
}
public static StringBuffer craeteStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb;
}
如我们新建的staticObject就是全局逃逸的。以及下面的方法中的sb对象,也是全局逃逸的。
public class ArgEscapeExample {
public void methodA() {
Object localObject = new Object();
methodB(localObject); // localObject作为参数传递,但不会从methodB中逃逸
}
public void methodB(Object param) {
// 在这里使用param
}
}
如传递到methodB中的param对象,就是发生了参数逃逸的。因为他从methodA中逃逸到了methodB中。
public static String createStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}
如上面的sb,就没有发生逃逸,因为这个对象本身没有作为参数传递,也没有被当做方法返回值,并没有赋值给静态变量。
在Java中,不同的逃逸状态影响JIT(即时编译器)的优化策略:
优化手段 | 全局逃逸 | 参数逃逸 | 无逃逸 |
---|---|---|---|
方法内联 | Y | Y | Y |
循环优化 | Y | Y | Y |
锁消除 | N | Y | Y |
栈上分配 | N | N | Y |
总的来说,JIT编译器根据对象的逃逸状态采用不同的优化策略,以提高Java程序的性能和效率。
在Java代码运行时,通过JVM参数可指定是否开启逃逸分析,
-XX:+DoEscapeAnalysis
: 表示开启逃逸分析
-XX:-DoEscapeAnalysis
: 表示关闭逃逸分析 从jdk 1.7开始已经默认开始逃逸分析,如需关闭,需要指定-XX:-DoEscapeAnalysis
关于逃逸分析的论文在1999年就已经发表了,但直到JDK 1.6才有实现,而且这项技术到如今也并不是十分成熟的。
其根本原因就是无法保证逃逸分析的性能消耗一定能高于他的消耗。虽然经过逃逸分析可以做标量替换、栈上分配、和锁消除。但是逃逸分析自身也是需要进行一系列复杂的分析的,这其实也是一个相对耗时的过程。
一个极端的例子,就是经过逃逸分析之后,发现没有一个对象是不逃逸的。那这个逃逸分析的过程就白白浪费掉了。
虽然这项技术并不十分成熟,但是他也是即时编译器优化技术中一个十分重要的手段。