JVM中的逃逸分析

逃逸分析(Escape Analysis)是目前Java虛擬機中比較前沿的優化技術。 java

逃逸分析的基本行爲就是分析對象動態做用域:當一個對象在方法中被定義後,它可能被外部方法所引用,例如做爲調用參數傳遞到其餘地方中,稱爲方法逃逸。 算法

例如: 併發

public static StringBuffer craeteStringBuffer(String s1, String s2) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        return sb;
    }
StringBuffer sb是一個方法內部變量,上述代碼中直接將sb返回,這樣這個StringBuffer有可能被其餘方法所改變,這樣它的做用域就不僅是在方法內部,雖然它是一個局部變量,稱其逃逸到了方法外部。

甚至還有可能被外部線程訪問到,譬如賦值給類變量或能夠在其餘線程中訪問的實例變量,稱爲線程逃逸。 app

上述代碼若是想要StringBuffer sb不逃出方法,能夠這樣寫: 高併發

public static String createStringBuffer(String s1, String s2) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        return sb.toString();
    }
不直接返回 StringBuffer,那麼StringBuffer將不會逃逸出方法。

若是能證實一個對象不會逃逸到方法或線程外,則可能爲這個變量進行一些高效的優化。
性能

1. 棧上分配

咱們都知道Java中的對象都是在堆上分配的,而垃圾回收機制會回收堆中再也不使用的對象,可是篩選可回收對象,回收對象還有整理內存都須要消耗時間。若是可以經過逃逸分析肯定某些對象不會逃出方法以外,那就可讓這個對象在棧上分配內存,這樣該對象所佔用的內存空間就能夠隨棧幀出棧而銷燬,就減輕了垃圾回收的壓力。 優化

在通常應用中,若是不會逃逸的局部對象所佔的比例很大,若是能使用棧上分配,那大量的對象就會隨着方法的結束而自動銷燬了。 spa

2. 同步消除

這一塊之前提到過,請參考[高併發Java 九] 鎖的優化和注意事項中 鎖消除 章節 .net

3. 標量替換

Java虛擬機中的原始數據類型(int,long等數值類型以及reference類型等)都不能再進一步分解,它們就能夠稱爲標量。相對的,若是一個數據能夠繼續分解,那它稱爲聚合量,Java中最典型的聚合量是對象。若是逃逸分析證實一個對象不會被外部訪問,而且這個對象是可分解的,那程序真正執行的時候將可能不建立這個對象,而改成直接建立它的若干個被這個方法使用到的成員變量來代替。拆散後的變量即可以被單獨分析與優化,能夠各自分別在棧幀或寄存器上分配空間,本來的對象就無需總體分配空間了。 線程

4. 總結

雖然概念上的JVM老是在Java堆上爲對象分配空間,但並非說徹底依照概念的描述去實現;只要最後實現處理的「可見效果」與概念中描述的一直就沒問題了。因此說,「you can cheat as long as you don't get caught」。Java對象在實際的JVM實現中可能在GC堆上分配空間也可能在棧上分配空間也可能徹底就消失了。這種行爲從Java源碼中看不出來,也沒法顯式指定,只是聰明的JVM自動作的優化而已。

可是逃逸分析會有時間消耗,因此性能未必提高多少,而且因爲逃逸分析比較耗時,目前的實現都是採用不那麼準確可是時間壓力相對較小的算法來完成逃逸分析,這就可能致使效果不穩定,要慎用

因爲HotSpot虛擬機目前的實現方法致使棧上分配實現起來比較複雜,由於在HotSpot中暫時尚未作這項優化。

Reference:

1. http://rednaxelafx.iteye.com/blog/659108/
相關文章
相關標籤/搜索