Java中的方法內(nèi)聯(lián)介紹
1. 什么是方法內(nèi)聯(lián)
例如有下面的原始代碼:
static class B {
int value;
final int get() {
return value;
}
}
public void foo() {
y = b.get();
// ...do stuff...
z = b.get();
sum = y + z;
}
我們首先要進(jìn)行的就是方法內(nèi)聯(lián),主要有下面兩個目的:
去除方法調(diào)用的成本,如查找方法版本、建立棧幀。
為其他優(yōu)化建立良好基礎(chǔ)。
內(nèi)聯(lián)后代碼如下:
public void foo() {
y = b.value;
// ...do stuff...
z = b.value;
sum = y + z;
}
后續(xù),還可以進(jìn)行冗余訪問消除、復(fù)寫傳播、無用代碼消除等優(yōu)化操作。
2. 方法內(nèi)聯(lián)的重要性
方法內(nèi)聯(lián)是編譯器最重要的優(yōu)化手段,如果沒有內(nèi)聯(lián),多數(shù)其他優(yōu)化都無法有效進(jìn)行。例如下面這個例子:
public static void foo(Object obj){
if (obj != null) {
System.out.println("do something");
}
}
public static void testInline(String[] args) {
Object obj = null;
foo(obj);
}
testInline()方法里其實(shí)全都是無用的代碼,但是如果不做方法內(nèi)聯(lián),就無法發(fā)現(xiàn)任何 Dead Code 的存在,因?yàn)榉珠_看的話兩個方法里面的操作可能都有意義。
3. Java中方法內(nèi)聯(lián)的困難
在 JVM 中,只有非虛方法,也就是使用invokespecial指令調(diào)用的私有方法、實(shí)例構(gòu)造器、父類方法和使用invokestatic指令調(diào)用的靜態(tài)方法才會在編譯器進(jìn)行解析。
而其他虛方法被invokevirtual指令調(diào)用,在調(diào)用時(shí)必須進(jìn)行方法接收者的多態(tài)選擇。對于一個虛方法,編譯器靜態(tài)地去做內(nèi)聯(lián)的時(shí)候很難確定應(yīng)該使用哪個方法版本,這就造成了方法內(nèi)聯(lián)的困難。
繼承類型關(guān)系分析 CHA
首先,JVM 引入了一種名為類型繼承關(guān)系分析 CHA 的技術(shù),這種技術(shù)用于在已加載的類中,確定某個接口是否有多于一種的實(shí)現(xiàn)、某個類是否存在子類、某個子類是否覆蓋了父類的某個虛方法等信息。
編譯器在進(jìn)行內(nèi)聯(lián)時(shí)會分不同情況采取不同處理:
如果是非虛方法,那么就直接進(jìn)行內(nèi)聯(lián)。
如果是虛方法,那么向 CHA 查詢是否有多個目標(biāo)版本可供選擇。
如果只有一個版本,就直接內(nèi)聯(lián),稱為守護(hù)內(nèi)聯(lián)。但由于 Java 程序動態(tài)連接,不知道什么時(shí)候就會加載到新的類型而改變 CHA 的結(jié)論,所以要留好逃生門,假如程序后續(xù)執(zhí)行中加載了導(dǎo)致繼承關(guān)系發(fā)生變化的新類,那么必須拋棄已經(jīng)編譯的代碼,退回到解釋狀態(tài)進(jìn)行執(zhí)行,或者重新編譯。
如果有多個版本可供選擇,那即時(shí)編譯器使用內(nèi)聯(lián)緩存來縮減方法調(diào)用的開銷。內(nèi)聯(lián)緩存是一個建立在目標(biāo)方法正常入口之前的緩存。在未發(fā)生方法調(diào)用時(shí),內(nèi)聯(lián)緩存為空。第一次調(diào)用發(fā)生后,緩存記錄下方法接收者的版本信息,并且在每次進(jìn)行調(diào)用前都檢查版本。
如果每次調(diào)用的方法接收者版本是一樣的,那稱為單態(tài)內(nèi)聯(lián)緩存,通過緩存來調(diào)用,相比不內(nèi)聯(lián)只多了一次類型判斷的開銷。如果出現(xiàn)方法接收者不一致的情況,就退化為超多態(tài)內(nèi)聯(lián)緩存,開銷相當(dāng)于真正查找虛方法表來進(jìn)行方法分派。當(dāng)緩存未命中的時(shí)候,大多數(shù)JVM的實(shí)現(xiàn)時(shí)退化成超多態(tài)內(nèi)聯(lián)緩存,也有一些JVM選擇重寫單態(tài)內(nèi)聯(lián)緩存,就是更新緩存為新的版本。這樣做的好處是以后還可能會命中,壞處是可能白白浪費(fèi)一個寫的開銷。

總結(jié)
到此這篇關(guān)于Java中的方法內(nèi)聯(lián)介紹的文章就介紹到這了,更多相關(guān)Java方法內(nèi)聯(lián)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中l(wèi)ogback?自動刷新不生效的問題解決
本文主要介紹了Java中l(wèi)ogback?自動刷新不生效的問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
java實(shí)現(xiàn)微信App支付服務(wù)端
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)微信App支付服務(wù)端,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10
java實(shí)現(xiàn)簡易飛機(jī)大戰(zhàn)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡易飛機(jī)大戰(zhàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
java通過cglib動態(tài)生成實(shí)體bean的操作
這篇文章主要介紹了java通過cglib動態(tài)生成實(shí)體bean的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02

