Java内存对象的逃逸分析

    逃逸分析英文作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结构。
    前面一篇文章《 Java并发编程-常量对象(七)》就曾讨论过这个问题,这是Swing内存和性能消耗的瓶颈。近几年业界曾进行过激烈讨论,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的加速功能,光这三种平台性能的提升就会给Swing带来又一次性能的革命。

你可能感兴趣的:(java,逃逸分析)