方法內聯的思想是,把目標方法的程式碼複製代發起呼叫的方法之中,避免發生真實的方法呼叫。
public class InlineTest {
private static int add1(int x1, int x2, int x3, int x4) {
return add2(x1, x2) + add2(x3, x4);
}
private static int add2(int x1, int x2) {
return x1 + x2;
}
}
如上程式碼,我們知道執行緒執行方法時,會向虛擬機器棧壓入棧幀,add1方法中呼叫了兩次add2方法會壓入兩次add2的棧幀。頻繁出入棧操作,消耗記憶體和時間。
JVM可以對上面的操作進行方法內聯優化,優化為下面程式碼。
private static int add1(int x1, int x2, int x3, int x4) {
return x1 + x2 + x3 + x4;
}
方法內聯的條件有兩個:
-
方法體足夠小。
- 熱點方法,如果方法體小於325位元組會嘗試內聯,可以使用
-XX:FreqInlineSize
修改大小。 - 非熱點方法,如果方法體小於35位元組嘗試內聯,
-XX:MaxInlineSize
。
- 熱點方法,如果方法體小於325位元組會嘗試內聯,可以使用
-
被呼叫的方法在執行時的實現可以被唯一確認。
- static、private、final方法,JIT可以唯一確認具體的實現程式碼。
- public例項方法,指向的實現可能是自身、父類、子類的程式碼(多型),只有當JIT唯一確認方法實現時,才有可能內聯。
內聯可能帶來的問題:會導致方法變大,使得CodeCache溢位,導致JVM退化成解釋執行模式。
一般情況,使用預設JVM引數就好。
測試方法內聯
@Slf4j
public class InlineTest {
private static int add1(int x1, int x2, int x3, int x4) {
return add2(x1, x2) + add2(x3, x4);
}
private static int add2(int x1, int x2) {
return x1 + x2;
}
private static long compute() {
long start = System.currentTimeMillis();
int result = 0;
Random random = new Random();
for (int i = 0; i < 10000000; i++) {
result = add1(random.nextInt(), random.nextInt(), random.nextInt(), random.nextInt());
}
long end = System.currentTimeMillis();
return end - start;
}
public static void main(String[] args) {
long compute = compute();
log.info("花費{}ms", compute);
}
}
花費362ms
花費483ms
設定JVM引數,列印內聯日誌,開關內聯(通過設定內聯閾值)。
-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:FreqInlineSize=1
預設開啟方法內聯,比直接關掉執行更快。