1. 程式人生 > 實用技巧 >【深入理解Java 虛擬機器之 即時編譯器】1141 編譯器優化技術

【深入理解Java 虛擬機器之 即時編譯器】1141 編譯器優化技術

一、概述

1、編譯器的目標雖然是做由程式程式碼翻譯為本地機器碼的工作,但其實難點並不在於能不能成功翻譯出機器碼,輸出程式碼優化質量的高低才是決定編譯器優秀與否的關鍵。

二、優化技術概覽

1、OpenJDK的官方Wiki上,HotSpot虛擬機器設計團隊列出了一個相對比較全面的、即時編譯器中採用的優化技術列表。
在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

2、即時編譯器對這些程式碼優化變換是建立在程式碼的中間表示或者是機器碼之上的,不是直接在Java原始碼上去做的。

3、各種編譯器一般都會把內聯優化放在優化序列最靠前的位置。

三、四種代表性優化技術學習

1、最重要的優化技術之一:方法內聯。

2、最前沿的優化技術之一:逃逸分析。

3、語言無關的經典優化技術之一:公共子表示式消除。

4、語言相關的經典優化技術之一:陣列邊界檢查消除。

四、方法內聯

1、內聯被業內戲稱為優化之母,因為除了消除方法呼叫的成本之外,它更重要的意義是為其他優化手段建立良好的基礎。

2、沒有內聯,多數其他優化都無法有效進行。

3、方法內聯的優化行為理解起來是沒有任何困難的,就是把目標方法的程式碼原封不動地“複製”到發起呼叫的方法之中,避免發生真實的方法呼叫而已。

4、Java方法解析和分派呼叫的時候

A: 只有使用invokespecial指令呼叫的私有方法、例項構造器、父類方法和使用invokestatic指令呼叫的靜態方法才會在編譯期進行解析。除了上述四種方法之外(最多再除去被final修飾的方法這種特殊情況,儘管它使用invokevirtual指令呼叫,但也是非虛方法。

B: Java語言中預設的例項方法是虛方法

5、Java 虛擬機器如何解決虛擬機器中虛擬方法的內聯問題

A: 引入了一種名為型別繼承關係分析(Class Hierarchy Analysis,CHA)的技術,這是整個應用程式範圍內的型別分析技術,用於確定在目前已載入的類中,某個介面是否有多於一種的實現、某個類是否存在子類、某個子類是否覆蓋了父類的某個虛方法等資訊。

B: 編譯器在進行內聯時就會分不同情況採取不同的處理:如果是非虛方法,那麼直接進行內聯就可以了,這種的內聯是有百分百安全保障的;如果遇到虛方法,則會向CHA查詢此方法在當前程式狀態下是否真的有多個目標版本可供選擇,如果查詢到只有一個版本,那就可以假設“應用程式的全貌就是現在執行的這個樣子”來進行內聯,這種內聯被稱為守護內聯(Guarded Inlining)。

C: 由於Java程式是動態連線的,說不準什麼時候就會載入到新的型別從而改變CHA結論,因此這種內聯屬於激進預測性優化,必須預留好“逃生門”,即當假設條件不成立時的“退路”(Slow Path)。假如在程式的後續執行過程中,虛擬機器一直沒有載入到會令這個方法的接收者的繼承關係發生變化的類,那這個內聯優化的程式碼就可以一直使用下去。如果載入了導致繼承關係發生變化的新類,那麼就必須拋棄已經編譯的程式碼,退回到解釋狀態進行執行,或者重新進行編譯。

6、內聯緩衝

A: 內聯快取是一個建立在目標方法正常入口之前的快取,它的工作原理大致為:在未發生方法呼叫之前,內聯快取狀態為空,當第一次呼叫發生後,快取記錄下方法接收者的版本資訊,並且每次進行方法呼叫時都比較接收者的版本。

B: 如果以後進來的每次呼叫的方法接收者版本都是一樣的,那麼這時它就是一種單態內聯快取(Monomorphic Inline Cache)。通過該快取來呼叫,比用不內聯的非虛方法呼叫,僅多了一次型別判斷的開銷而已。

C: 如果真的出現方法接收者不一致的情況,就說明程式用到了虛方法的多型特性,這時候會退化成超多型內聯快取(Megamorphic Inline Cache),其開銷相當於真正查詢虛方法表來進行方法分派。

五、逃逸分析

逃逸分析(Escape Analysis)是目前Java虛擬機器中比較前沿的優化技術,它與型別繼承關係分析一樣,並不是直接優化程式碼的手段,而是為其他優化措施提供依據的分析技術。

2、基本原理是:分析物件動態作用域,當一個物件在方法裡面被定義後,它可能被外部方法所引用,例如作為呼叫引數傳遞到其他方法中,這種稱為方法逃逸;甚至還有可能被外部執行緒訪問到,譬如賦值給可以在其他執行緒中訪問的例項變數,這種稱為執行緒逃逸;從不逃逸、方法逃逸到執行緒逃逸,稱為物件由低到高的不同逃逸程度。

3、如果能證明一個物件不會逃逸到方法或執行緒之外(換句話說是別的方法或執行緒無法通過任何途徑訪問到這個物件),或者逃逸程度比較低(只逃逸出方法而不會逃逸出執行緒),則可能為這個物件例項採取不同程度的優化

A: 棧上分配[插圖](Stack Allocations):在Java虛擬機器中,Java堆上分配建立物件的記憶體空間,Java堆中的物件對於各個執行緒都是共享和可見的,只要持有這個物件的引用,就可以訪問到堆中儲存的物件資料。虛擬機器的垃圾收集子系統會回收堆中不再使用的物件,但回收動作無論是標記篩選出可回收物件,還是回收和整理記憶體,都需要耗費大量資源。如果確定一個物件不會逃逸出執行緒之外,那讓這個物件在棧上分配記憶體將會是一個很不錯的主意,物件所佔用的記憶體空間就可以隨棧幀出棧而銷燬。在一般應用中,完全不會逃逸的區域性物件和不會逃逸出執行緒的物件所佔的比例是很大的,如果能使用棧上分配,那大量的物件就會隨著方法的結束而自動銷燬了,垃圾收集子系統的壓力將會下降很多。棧上分配可以支援方法逃逸,但不能支援執行緒逃

B: 標量替換(Scalar Replacement):若一個數據已經無法再分解成更小的資料來表示了,Java虛擬機器中的原始資料型別(int、long等數值型別及reference型別等)都不能再進一步分解了,那麼這些資料就可以被稱為標量。相對的,如果一個數據可以繼續分解,那它就被稱為聚合量(Aggregate),Java中的物件就是典型的聚合量。如果把一個Java物件拆散,根據程式訪問的情況,將其用到的成員變數恢復為原始型別來訪問,這個過程就稱為標量替換。假如逃逸分析能夠證明一個物件不會被方法外部訪問,並且這個物件可以被拆散,那麼程式真正執行的時候將可能不去建立這個物件,而改為直接建立它的若干個被這個方法使用的成員變數來代替。將物件拆分後,除了可以讓物件的成員變數在棧上(棧上儲存的資料,很大機會被虛擬機器分配至物理機器的高速暫存器中儲存)分配和讀寫之外,還可以為後續進一步的優化手段建立條件。標量替換可以視作棧上分配的一種特例,實現更簡單(不用考慮整個物件完整結構的分配),但對逃逸程度的要求更高,它不允許物件逃逸出方法範圍內

C: 同步消除(Synchronization Elimination):執行緒同步本身是一個相對耗時的過程,如果逃逸分析能夠確定一個變數不會逃逸出執行緒,無法被其他執行緒訪問,那麼這個變數的讀寫肯定就不會有競爭,對這個變數實施的同步措施也就可以安全地消除掉。

4、逃逸分析的計算成本非常高,甚至不能保證逃逸分析帶來的效能收益會高於它的消耗。如果要百分之百準確地判斷一個物件是否會逃逸,需要進行一系列複雜的資料流敏感的過程間分析,才能確定程式各個分支執行時對此物件的影響。

5、可以使用引數-XX:+DoEscapeAnalysis來手動開啟逃逸分析,開啟之後可以通過引數-XX:+PrintEscapeAnalysis來檢視分析結果。有了逃逸分析支援之後,使用者可以使用引數-XX:+EliminateAllocations來開啟標量替換,使用+XX:+EliminateLocks來開啟同步消除,使用引數-XX:+PrintEliminateAllocations檢視標量的替換情況。

6、逃逸分析技術肯定會支撐起一系列更實用、有效的優化技術。

六、公共子字表達式消除

1、公共子表示式消除是一項非常經典的、普遍應用於各種編譯器的優化技術,它的含義是:如果一個表示式E之前已經被計算過了,並且從先前的計算到現在E中所有變數的值都沒有發生變化,那麼E的這次出現就稱為公共子表示式。

2、這種表示式,沒有必要花時間再對它重新進行計算,只需要直接用前面計算過的表示式結果代替E。如果這種優化僅限於程式基本塊內,便可稱為區域性公共子表示式消除(Local Common Subexpression Elimination),如果這種優化的範圍涵蓋了多個基本塊,那就稱為全域性公共子表示式消除(Global Common SubexpressionElimination)。

七、陣列邊界檢查消除

1、陣列邊界檢查消除(Array Bounds Checking Elimination)是即時編譯器中的一項語言相關的經典優化技術。

在Java語言中訪問陣列元素foo[i]的時候系統將會自動進行上下界的範圍檢查,即i必須滿足“i>=0&&i<foo.length”的訪問條件,否則將丟擲一個執行時異常:java.lang.ArrayIndexOutOfBoundsException。這對軟體開發者來說是一件很友好的事情,即使程式設計師沒有專門編寫防禦程式碼,也能夠避免大多數的溢位攻擊。但是對於虛擬機器的執行子系統來說,每次陣列元素的讀寫都帶有一次隱含的條件判定操作,對於擁有大量陣列訪問的程式程式碼,這必定是一種效能負擔

2、自動裝箱消除(Autobox Elimination)、安全點消除(Safepoint Elimination)、消除反射(Dereflection)等