1. 程式人生 > >JVM 之 逃逸分析和TLAB

JVM 之 逃逸分析和TLAB

來看一道面試題:所有的new 物件都是分配在堆上的嗎?如果不是,是什麼情況。

在沒看到這道題目的時候,我對所有物件都分配在堆上的想法是沒有一絲懷疑的,但是事實是不一定的。

逃逸分析

逃逸分析的定義

逃逸分析,是一種可以有效減少Java 程式中同步負載和記憶體堆分配壓力的跨函式全域性資料流分析演算法。通過逃逸分析,Java Hotspot編譯器能夠分析出一個新的物件的引用的使用範圍從而決定是否要將這個物件分配到堆上。

Java在Java SE 6u23以及以後的版本中支援並預設開啟了逃逸分析的選項。Java的 HotSpot JIT編譯器,能夠在方法過載或者動態載入程式碼的時候對程式碼進行逃逸分析,同時Java物件在堆上分配和內建執行緒的特點使得逃逸分析成Java的重要功能。

逃逸分析是如何決定一個物件是否分配在堆

先來看下面這段程式

public class test {

    public static void main(String[] args){
        String str = new String("Test");
        System.out.println(str);
    }
}

花兩分鐘想想,如果你是jvm的設計師,你會把這個str分配在堆上嗎?

jvm中的堆上的物件可以執行緒共享的,那麼我們把str放在堆上,誰來共享呢?

很明顯沒有人會再使用它,它在自己的執行緒棧結束的時候,就已經沒有意義了。

再來看下面這段程式:

public class test {

    public static String getString(){
        String str = new String("Test");
        return str;
    }

    public static void main(String[] args){
        String MyStr = getString();
        System.out.println(MyStr);
    }
}

在呼叫getString方法的時候,在該棧幀上,如果我們把str放在棧中,那麼呼叫該方法體將得不到該物件。因為在getString結束的時候,就會把str物件釋放,那麼在main方法體中,MyStr將會指向null。

於是在這個情況下,str物件是會被放在堆上去共享的。我想看到這,大家已經明白那些物件是可以放在棧上,哪些是必須放在堆上的了。

若一個方法體內的物件不被其他方法或者執行緒得到,我們可以把物件直接存放在棧上,當JVM能證明一個物件不會逃逸到方法或者執行緒外,則可能為這個變數進行一些高效的優化。

把物件放在棧上有什麼意義

我們知道在堆上的物件是被多個執行緒共享的,共享就要考慮多執行緒的安全問題,那麼就需要鎖的消耗。而且把物件放在棧上,會隨著棧的出棧一起釋放。減輕GC的壓力。

TLAB

JVM在記憶體新生代Eden Space中開闢了一小塊執行緒私有的區域,稱作TLAB(Thread-local allocation buffer)。預設設定為佔用Eden Space的1%。在Java程式中很多物件都是小物件且用過即丟,它們不存線上程共享也適合被快速GC,所以對於小物件通常JVM會優先分配在TLAB上,並且TLAB上的分配由於是執行緒私有所以沒有鎖開銷。因此在實踐中分配多個小物件的效率通常比分配一個大物件的效率要高。

但物件還是分配在堆上的,只不過是堆上的一塊區域而已。