JVM中的逃逸分析
逃逸分析(Escape Analysis)是目前Java虛擬機中比較前沿的優(yōu)化技術。
逃逸分析的基本行為就是分析對象動態(tài)作用域:當一個對象在方法中被定義后,它可能被外部方法所引用,例如作為調用參數傳遞到其他地方中,稱為方法逃逸。
例如:
| 1 2 3 4 5 6 | public static StringBuffer craeteStringBuffer(String s1, String s2) { ????????StringBuffer sb = new StringBuffer(); ????????sb.append(s1); ????????sb.append(s2); ????????return sb; ????} |
StringBuffer sb是一個方法內部變量,上述代碼中直接將sb返回,這樣這個StringBuffer有可能被其他方法所改變,這樣它的作用域就不只是在方法內部,雖然它是一個局部變量,稱其逃逸到了方法外部。
甚至還有可能被外部線程訪問到,譬如賦值給類變量或可以在其他線程中訪問的實例變量,稱為線程逃逸。
上述代碼如果想要StringBuffer sb不逃出方法,可以這樣寫:
| 1 2 3 4 5 6 | public static String createStringBuffer(String s1, String s2) { ????????StringBuffer sb = new StringBuffer(); ????????sb.append(s1); ????????sb.append(s2); ????????return sb.toString(); ????} |
不直接返回?StringBuffer,那么StringBuffer將不會逃逸出方法。
如果能證明一個對象不會逃逸到方法或線程外,則可能為這個變量進行一些高效的優(yōu)化。
1. 棧上分配
我們都知道Java中的對象都是在堆上分配的,而垃圾回收機制會回收堆中不再使用的對象,但是篩選可回收對象,回收對象還有整理內存都需要消耗時間。如果能夠通過逃逸分析確定某些對象不會逃出方法之外,那就可以讓這個對象在棧上分配內存,這樣該對象所占用的內存空間就可以隨棧幀出棧而銷毀,就減輕了垃圾回收的壓力。
在一般應用中,如果不會逃逸的局部對象所占的比例很大,如果能使用棧上分配,那大量的對象就會隨著方法的結束而自動銷毀了。
2. 同步消除
這一塊以前提到過,請參考[高并發(fā)Java 九] 鎖的優(yōu)化和注意事項中 鎖消除 章節(jié)
3. 標量替換
Java虛擬機中的原始數據類型(int,long等數值類型以及reference類型等)都不能再進一步分解,它們就可以稱為標量。相對的,如果一個數據可以繼續(xù)分解,那它稱為聚合量,Java中最典型的聚合量是對象。如果逃逸分析證明一個對象不會被外部訪問,并且這個對象是可分解的,那程序真正執(zhí)行的時候將可能不創(chuàng)建這個對象,而改為直接創(chuàng)建它的若干個被這個方法使用到的成員變量來代替。拆散后的變量便可以被單獨分析與優(yōu)化,可以各自分別在棧幀或寄存器上分配空間,原本的對象就無需整體分配空間了。
4. 總結
雖然概念上的JVM總是在Java堆上為對象分配空間,但并不是說完全依照概念的描述去實現;只要最后實現處理的“可見效果”與概念中描述的一直就沒問題了。所以說,“you can cheat as long as you don’t get caught”。Java對象在實際的JVM實現中可能在GC堆上分配空間,也可能在棧上分配空間,也可能完全就消失了。這種行為從Java源碼中看不出來,也無法顯式指定,只是聰明的JVM自動做的優(yōu)化而已。
但是逃逸分析會有時間消耗,所以性能未必提升多少,并且由于逃逸分析比較耗時,目前的實現都是采用不那么準確但是時間壓力相對較小的算法來完成逃逸分析,這就可能導致效果不穩(wěn)定,要慎用。
由于HotSpot虛擬機目前的實現方法導致棧上分配實現起來比較復雜,因為在HotSpot中暫時還沒有做這項優(yōu)化。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現金大獎總結
- 上一篇: 避免在循环体中声明创建对象
- 下一篇: elasticsearch-jdbc同步