一、概念
方法內聯就是把調用方函數代碼"復制"到調用方函數中,減少因函數調用開銷的技術
函數調用過程
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}使用,並且只能加在其后才能生效)