逃逸分析英文作Escape Analysis。在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。
当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他过程或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。
在Java中比如下面的方法:
......
static V global_v;
public void a_method(){
V v=b_method();
c_method();
}
public V b_method(){
V v=new V();
return v;
}
public void c_method(){
global_v=new V();
}
其中b_method方法内部生成的V对象的引用被返回给a_method方法内的变量v,c_method方法内生成的V对象被赋给了全局变量global_v。这两种场景都发生了指针(引用)逃逸。
逃逸分析研究对于Java编译器有什么好处?我们知道Java对象总是在堆中分配的,因此Java对象的创建和回收对系统的开销是很大的。Java语言被批评的一个地方,也是认为Java性能慢的一个原因就是Java不支持运行时栈分配对象,缺少像C#里面的值对象或者C++里面的struct结构。还有就是JVM在构造对象的时候有可能发生partial construct,就是说其他的Thread可以看到这个reference,但是对象却没有构造完全。而现代的编译器(JIT)可以使用out of ordering来调度指令,可能就把reference的赋值放在构造函数前边。这篇文章给除了很好的分析:
http://www.ibm.com/developerworks/java/library/j-dcl.html。
Java 6中是否应该加入栈分配对象。其中有人主张可以通过JIT进行逃逸分析的方式来解决目前的问题,不用在语言级别进行支持,虚拟机支持栈分配机制,由JIT对代码进行内联优化和逃逸分析。
那么JIT怎么通过逃逸分析进行代码优化呢?分析下面的过程代码:
public void my_method(){
V v=new V();
//use v
......
v=null;
}
在这个方法中创建的局部对象被赋给了v,但是没有返回,没有赋给全局变量等等操作,因此这个对象是没有逃逸的,是可以在运行时栈进行分配和销毁的对象。没有发生逃逸的对象由于生命周期都在一个方法体内,因此它们是可以在运行时栈上分配并销毁。
这样在JIT编译Java伪代码时,如果能分析出这种代码,那么非逃逸对象其创建和回收就可以在栈上进行,从而能大大提高Java的运行性能。
另外为什么要在逃逸分析之前进行内联分析呢?这是因为往往有些对象在被调用过程中创建并返回给调用过程,调用过程使用完该对象就销毁了。这种情况下如果将这些方法进行内联,它们就由两个方法体变成一个方法体了,这种原来通过返回传递的对象就变成了方法内的局部对象,就变成了非逃逸对象了,这样这些对象就可以在同一栈上进行分配了。
据说Java 6的虚拟机已经支持对象的栈分配和逃逸分析机制了,但目前并没有启动。具可靠的消息,Java 7中将启动这一功能。这对Swing来说又是一次大规模提升速度的机会。
除能将堆分配对象变成栈分配对象,逃逸分析还有其他两个优化应用。一是同步消除。我们知道线程同步的代价是相当高的,同步的后果是降低并发性和性能。逃逸分析可以判断出某个对象是否始终只被一个线程访问,如果只被一个线程访问,那么对该对象的同步操作就可以转化成没有同步保护的操作,这样就能大大提高并发程度和性能。
二是矢量替代。逃逸分析方法如果发现对象的内存存储结构不需要连续进行的话,就可以将对象的部分甚至全部都保存在CPU寄存器内,这样能大大提高访问速度。
Java 7将完全支持了栈式分配对象,JIT将支持逃逸分析优化,另外Java 7还将缺省支持OpenGL的加速功能。
JVM中启用逃逸分析 DoEscapeAnalysis
安装jdk1.6.0_14,运行java时传递jvm参数 -XX:+DoEscapeAnalysis
逃逸分析还能用于以下优化场景,但在JVM中未知使用。
1,标量替换(Scalar Replacement)
2,减小竞争检测范围
3,基于区域的内存分配