Java的逃逸分析技术

使用内存逃逸分析技术,编译器会对代码做如下优化

  1. 同步省略。如果一个对象被发现只能从一个线程被访问到。那么对于这个对象的操作可以不考虑同步。
  2. 将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使得指向该对象的指针永远不会逃逸,对象可能是栈分配的时候选的,而不是堆分配
  3. 分离对象或者标量替换。有的对象可能不需要作为一个连续的内存结构存在也可以访问到。那么对象的部分可以不存储在内存中,而是存储在CPU的寄存器中。

同步省略

在动态编译同步块的时候,JIT编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有发布到其他线程。

如果同步块的所使用的锁对象通过这种分析被证实只能够被一个线程访问。那么JIT编译器会在编译的时候,把同步块的时候就会取消这部分的代码同步。这个取消同步的过程叫做同步省略,也叫锁消除功能。

那么在什么时候我们可以判断出某一块的代码是只能够被一个线程访问到的?

如下代码:

public void f() {

    Object hollis = new Object();

    synchronized(hollis) {

        System.out.println(hollis);

    }

}

代码中的hollis这个对象进行加锁。但是hollis对象的生命周期只在f()方法中,并不会被其他线程所访问,所以JIT编译阶段就会被优化掉。优化为:

public void f() {

    Object hollis = new Object();

    System.out.println(hollis);

}

标量替换

标量是指一个无法再次分解为更小的数据的数据。Java中的原始数据类型就是标量。相对的,那些可以分解的数据叫做聚合量,java中的对象就是聚合量。因为他可以分解为其他聚合量和标量。

在JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问到,那么经过JIT优化之后,就会把这个对象拆解为若干个其中包含的若干个成员变量来代替。这个过程叫标量替换。

public static void main(String[] args) {

   alloc();

}



private static void alloc() {

   Point point = new Point(1,2);

   System.out.println("point.x="+point.x+"; point.y="+point.y);

}

class Point{

    private int x;

    private int y;

}

以上代码中,point对象没有逃逸出alloc方法,并且point对象可以拆分为标量的。那么,JIT就不会直接创建POINT对象,而是直接使用两个标量int x,int y代替POINT对象。

所以,经过JIT优化之后。

private static void alloc() {

   int x = 1;

   int y = 2;

   System.out.println("point.x="+x+"; point.y="+y);

}

标量替换为栈上分配提供了很好的基础。

栈上分配

在Java中,我们都知道Java只能堆中进行分配内存,但是有一种情况,那就是经过逃逸分析之后,一个对象没有逃逸出方法,那么就有可能被优化为栈上分配。但是这也并不是绝对的。还有一点需要注意的是,JDK1.7之后就默认开启了逃逸分析技术。详见:http://www.hollischuang.com/archives/2398

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章