1. 程式人生 > >Java編譯優化技術之逃逸分析

Java編譯優化技術之逃逸分析

  逃逸分析(Escape Analysis)是Java虛擬機器中比較先進的編譯優化技術,它與型別繼承關係分析一樣,並不是直接優化程式碼的手段,而是為其他優化手段提供依據的分析技術。逃逸分析的基本行為就是分析物件動態作用域:當一個物件在方法裡面被定義後,它可能被外部方法所引用,例如作為呼叫引數傳遞到其他方法中,這種行為稱為方法逃逸。甚至還有可能被外部執行緒訪問到,譬如賦值給類變數或可以在其他執行緒中訪問的例項變數,這種行為稱為執行緒逃逸。

  如果能證明一個物件不會逃逸到方法或執行緒之外,也就是別的方法或執行緒無法通過任何途徑訪問到這個物件,則可能為這個變數進行一些高效的優化,如:
1、棧上分配(Stack Allocations): 
Java虛擬機器中,在Java堆上分配建立物件的記憶體空間幾乎是Java程式設計師都清楚的常識了,Java堆中的物件對於各個執行緒都是共享和可見的,只要持有這個物件的引用,就可以訪問堆中儲存的物件資料。虛擬機器的垃圾收集系統可以回收掉堆中不再使用的物件,但回收動作無論是篩選可回收物件,還是回收和整理記憶體都需要耗費時間。如果確定一個物件不會逃逸出方法之外,那讓這個物件在棧上分配記憶體將會是一個很不錯的主意,物件所佔用的記憶體空間就可以隨棧幀出棧而銷燬。在一般應用中,不會逃逸的區域性物件所佔的比率很大,如果能使用棧上分配,那大量的物件就會隨著方法的結束而自動銷燬了,垃圾收集系統的壓力將會小很多。
2、同步消除(Synchronization Elimination):
執行緒同步本身就是一個相對耗時的過程,如果逃逸分析能夠確定一個變數不會逃逸出執行緒,無法被其他執行緒訪問,那這個變數的讀寫肯定就不會有競爭,對這個變數實施的同步措施也就可以消除掉。
3、標量替換(Scalar Replacement):
標量(Scalar)是指一個數據已經無法再分解成更小的資料來表示了,Java虛擬機器中的原始資料型別(int、long等數值型別 及reference型別等)都不能再進一步分解,它們就可以被稱為標量。相對的,如果一個數據可以繼續分解,那它就被稱做聚合量(Aggregate), Java中的物件就是最典型的聚合量。如果把一個Java物件拆散,根據程式訪問的情況,將其使用到的成員變數恢復原始型別來訪問就叫做標量替換。如果逃逸分析證明一個物件不會被外部訪問,並且這個物件可以被拆散的話,那程式真正執行的時候將可能不建立這個物件,而改為直接建立它的若干個被這個方法使用到的成員變景來代替。將物件拆分後,除了可以讓物件的成員變數在棧上(棧上儲存的資料,很大機會會被虛擬機器分配至物理機器的高速暫存器中儲存)分配和讀寫之外,還可以為後續進一步的優化手段建立條件。

   逃逸分析在Sim JDK 1.6中實現,這項優化尚未成熟,仍有巨大的改進餘地。不成熟的原因主要是不能保證逃逸分析的效能收益必定高於它的消耗。如果要百分 之百準確地判斷一個物件是否會逃逸,需要進行資料流敏感的複雜分析,來確定程式各個分支執行時對此物件的影響。這是一個相對髙耗時的過程,如果分析完後發現沒有幾個不逃逸的物件,那時間就白白浪費了,所以目前虛擬機器只能採用不那麼準確,但時間壓力相對較小的演算法來完成逃逸分析。如果有需要,並且確認對程式執行有益,使用者可以使用引數-XX:+DoEscapeAnalysis來手動開啟逃逸分析,開啟之後可以通過引數-XX:+PrintEscapeAnalysis來査看分析結果。另外,使用者可以使用引數-XX:+EliminateAllocations來開啟標量替換,使用引數 +XX:+EliminateLocks來開啟同步消除,使用引數-XX:+PrintEliminateAllocations 來査看標量的替換情況。