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

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