兩條編碼準則與java編譯器

我們在寫代碼時,常常會提到兩條原則:
1、方法要儘量短,大方法要分解成小方法;
2、不要重複發明輪子。
我們在強調這兩個原則的時候,往往只關注的是代碼簡潔、易維護等方便我們人的因素,其實這樣做還可以大大方便java編譯器優化代碼。
java編譯器優化簡介:
Java 應用程序的編譯過程與靜態編譯語言(例如 C 或 C++)不同。靜態編譯器直接把源代碼轉換成可以直接在目標平臺上執行的機器代碼,不同的硬件平臺要求不同的編譯器。 Java 編譯器把 Java 源代碼轉換成可移植的JVM 字節碼。與靜態編譯器不同,javac 幾乎不做什麼優化,在靜態編譯語言中應當由編譯器進行的優化工作,在 Java 中是在程序執行的時候,由運行時執行優化。

即時編譯
對於證實概念的實現來說,解釋是合適的,但是早期的 JVM 由於太慢。下一代 JVM 使用即時 (JIT) 編譯器來提高執行速度。按照嚴格的定義,基於 JIT 的虛擬機在執行之前,把所有字節碼轉換成機器碼,但是以惰性方式來做這項工作:JIT 只有在確定某個代碼路徑將要執行的時候,才編譯這個代碼路徑(因此有了名稱“ 即時 編譯”)。這個技術使程序能啓動得更快,因爲在開始執行之前,不需要冗長的編譯階段。
JIT 技術看起來很有前途,但是它有一些不足。JIT 消除了解釋的負擔(以額外的啓動成本爲代價),但是由於若干原因,代碼的優化等級仍然是一般般。爲了避免 Java 應用程序嚴重的啓動延遲,JIT 編譯器必須非常迅速,這意味着它無法把大量時間花在優化上。所以,早期的 JIT 編譯器在進行內聯假設(inlining assumption)方面比較保守,因爲它們不知道後面可能要裝入哪個類。
雖然從技術上講,基於 JIT 的虛擬機在執行字節碼之前,要先編譯字節碼,但是 JIT 這個術語通常被用來表示任何把字節碼轉換成機器碼的動態編譯過程 —— 即使那些能夠解釋字節碼的過程也算。

HotSpot 動態編譯

HotSpot 執行過程組合了編譯、性能分析以及動態編譯。它沒有把所有要執行的字節碼轉換成機器碼,而是先以解釋器的方式運行,只編譯“熱門”代碼 —— 執行得最頻繁的代碼。當 HotSpot 執行時,會蒐集性能分析數據,用來決定哪個代碼段執行得足夠頻繁,值得編譯。只編譯執行最頻繁的代碼有幾項性能優勢:沒有把時間浪費在編譯那些不經常執行的代碼上;這樣,編譯器就可以花更多時間來優化熱門代碼路徑,因爲它知道在這上面花的時間物有所值。而且,通過延遲編譯,編譯器可以訪問性能分析數據,並用這些數據來改進優化決策,例如是否需要內聯某個方法調用。爲了讓事情變得更復雜,HotSpot 提供了兩個編譯器:客戶機編譯器和服務器編譯器。默認採用客戶機編譯器;在啓動 JVM 時,您可以指定 -server 開關,選擇服務器編譯器。服務器編譯器針對最大峯值操作速度進行了優化,適用於需要長期運行的服務器應用程序。客戶機編譯器的優化目標,是減少應用程序的啓動時間和內存消耗,優化的複雜程度遠遠低於服務器編譯器,因此需要的編譯時間也更少。

HotSpot 服務器編譯器能夠執行各種樣的類。它能夠執行許多靜態編譯器中常見的標準優化,例如代碼提升( hoisting)、公共的子表達式清除、循環展開(unrolling)、範圍檢測清除、死代碼清除、數據流分析,還有各種在靜態編譯語言中不實用的優化技術,例如虛方法調用的聚合內聯。

持續重新編譯

HotSpot 技術另一個有趣的方面是:編譯不是一個全有或者全無(all-or-nothing)的命題。在解釋代碼路徑一定次數之後,會把它重新編譯成機器碼。但是 JVM 會繼續進行性能分析,而且如果認爲代碼路徑特別熱門,或者未來的性能分析數據認爲存在額外的優化可能,那麼還有可能用更高一級的優化重新編譯代碼。JVM 在一個應用程序的執行過程中,可能會把相同的字節碼重新編譯許多次。爲了深入瞭解編譯器做了什麼,可以 -XX:+PrintCompilation 標誌調用 JVM,這個標誌會使編譯器(客戶機或服務器)每次運行的時候打印一條短消息。

棧上(On-stack)替換

HotSpot 開始的版本編譯的時候每次編譯一個方法。如果某個方法的累計執行次數超過指定的循環迭代次數(在 HotSpot 的第一版中,是 10,000 次),那麼這個方法就被當作熱門方法,計算的方式是:爲每個方法關聯一個計數器,每次執行一個後向分支時,就會遞增計數器一次。但是,在方法編譯之後,方法調用並沒有切換到編譯的版本,需要退出並重新進入方法,後續調用纔會使用編譯的版本。結果就是,在某些情況下,可能永遠不會用到編譯的版本,例如對於計算密集型程序,在這類程序中所有的計算都是在方法的一次調用中完成的。重量級方法可能被編譯,但是編譯的代碼永遠用不到。

HotSpot 最近的版本採用了稱爲 棧上(on-stack)替換 (OSR) 的技術,支持在循環過程中間,從解釋執行切換到編譯的代碼(或者從編譯代碼的一個版本切換到另一個版本)。

從java編譯、執行優化的原理可以看出,編譯器會將“熱門代碼塊”、“熱門方法”持續優化,以提高性能,再回顧我們常常強調的兩個原則:
1、儘量寫小方法。小方法意味着功能單一、重用性高,自然會被很多地方用到,容易變成“熱門方法”。
2、不重複發明輪子,儘量用已存在的輪子。大家共用一個“輪子”,自然就是“熱門”輪子,編譯器會知道這個輪子要好好優化,讓他賺的更快。
發佈了58 篇原創文章 · 獲贊 0 · 訪問量 3360
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章