1. 程式人生 > >Java方法內聯

Java方法內聯

一、概念 方法內聯就是把呼叫方函式程式碼"複製"到呼叫方函式中,減少因函式呼叫開銷的技術   函式呼叫過程 1、首先會有個執行棧,儲存它們的區域性變數、方法名、動態連線 2、當一個方法被呼叫,一個新的棧幀會被加到棧頂,分配的本地變數和引數會儲存在這個棧幀 3、跳轉到目標方法程式碼執行 4、方法返回的時候,本地方法和引數被銷燬,棧頂被移除 5、返回原來的地址執行   注:這就是通常說的函式呼叫的壓棧和出棧過程,因此,函式呼叫需要有一定的時間開銷和空間開銷,當一個方法體不大,但又頻繁被呼叫時,這個時間和空間開銷會相對變得很大,變得非常不划算,同時降低了程式的效能。根據二八原則,80%的效能消耗其實是發生在20%的程式碼上,對熱點程式碼的針對性優化可以提升整體系統的效能   二、方法內聯的原理 就如上面所說,就是把呼叫方函式程式碼"複製"到呼叫方函式中   看如下例子:
private int add2(int x1 , int x2 , int x3 , int x4) {
return add1(x1 , x2) + add1(x3,x4);
}
 
private int add1(int x1 , int x2) {
return x1 + x2;
}

  

  執行一段時間後,程式碼被內聯翻譯成:
private int add2(int x1 , int x2 , int x3 , int x4) {
//return add1(x1 , x2) + add1(x3,x4);
return x1 + x2 + x3 + x4;
}

 

  三、方法內聯的條件 JVM會自動的識別熱點方法,並對它們使用方法內聯優化。那麼一段程式碼需要執行多少次才會觸發JIT優化呢?通常這個值由-XX:CompileThreshold引數進行設定:   使用client編譯器時,預設為1500;     使用server編譯器時,預設為10000; 但是一個方法就算被JVM標註成為熱點方法,JVM仍然不一定會對它做方法內聯優化。其中有個比較常見的原因就是這個方法體太大了,分為兩種情況。    如果方法是經常執行的,預設情況下,方法大小小於325位元組的都會進行內聯(可以通過** -XX:MaxFreqInlineSize=N**來設定這個大小)        如果方法不是經常執行的,預設情況下,方法大小小於35位元組才會進行內聯(可以通過** -XX:MaxInlineSize=N **來設定這個大小)   我們可以通過增加這個大小,以便更多的方法可以進行內聯;但是除非能夠顯著提升效能,否則不推薦修改這個引數。因為更大的方法體會導致程式碼記憶體佔用更多,更少的熱點方法會被快取,最終的效果不一定好。   JVM引數:(-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining) 當我們執行1000次
 

 

當我們執行10001次

 

  四、內聯的隱藏條件   雖然JIT號稱可以針對程式碼全域性的執行情況而優化,但是JIT對一個方法內聯之後,還是可能因為方法被繼承,導致需要型別檢查而沒有達到效能的效果 想要對熱點的方法使用上內聯的優化方法,最好儘量使用final、private、static這些修飾符修飾方法,避免方法因為繼承,導致需要額外的型別檢查,而出現效果不好情況。   五、結論   1、針對熱點方法,想要通過JIT內聯優化來提升效能的建議  2、更小的方法體,JVM總是偏好更小的方法。 3、儘量使用final、private、static修飾符 4、使用+PrintInlining引數校驗效果(【PrintInlining】-XX:+UnlockDiagnosticVMOptions必須配合引數{2}使用,並且只能加在其後才能生效)