Java方法內聯
阿新 • • 發佈:2018-12-20
一、概念
方法內聯就是把呼叫方函式程式碼"複製"到呼叫方函式中,減少因函式呼叫開銷的技術
函式呼叫過程
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}使用,並且只能加在其後才能生效)