逃逸分析
逃逸分析是“一種確定指針動(dòng)態(tài)范圍的靜態(tài)分析缚陷,它可以分析在程序的哪些地方可以訪問到指針”
- 在 Java 虛擬機(jī)的即時(shí)編譯語境下,逃逸分析將判斷新建的對(duì)象是否逃逸贤重,即時(shí)編譯器判斷對(duì)象是否逃逸的依據(jù)有:
- 對(duì)象是否被存入堆中(靜態(tài)字段或者堆中對(duì)象的實(shí)例字段)
- 一旦對(duì)象被存入堆中础废,其他線程便能獲得該對(duì)象的引用。即時(shí)編譯器也因此無法追蹤所有使用該對(duì)象的代碼位置
- 對(duì)象是否被傳入未知代碼中
- Java 虛擬機(jī)的即時(shí)編譯器是以方法為單位的崖飘,對(duì)于方法中未被內(nèi)聯(lián)的方法調(diào)用,即時(shí)編譯器會(huì)將其當(dāng)成未知代碼
- 即時(shí)編譯器里的逃逸分析是放在方法內(nèi)聯(lián)之后的杈女,以便消除這些“未知代碼”入口
- 對(duì)象是否被存入堆中(靜態(tài)字段或者堆中對(duì)象的實(shí)例字段)
基于逃逸分析的優(yōu)化
- 鎖消除
- 如果即時(shí)編譯器能夠證明鎖對(duì)象不逃逸朱浴,那么對(duì)該鎖對(duì)象的加鎖、解鎖操作沒有意義
- 即時(shí)編譯器可以消除對(duì)該不逃逸鎖對(duì)象的加鎖达椰、解鎖操作
- synchronized (new Object()) {}會(huì)被完全優(yōu)化掉翰蠢,因?yàn)榛谔右莘治龅逆i消除
- synchronized (escapedObject) {}則不然,由于其他線程可能會(huì)對(duì)逃逸了的對(duì)象escapedObject進(jìn)行加鎖操作啰劲,從而構(gòu)造了兩個(gè)線程之間的 happens-before 關(guān)系梁沧。因此即時(shí)編譯器至少需要為這段代碼生成一條刷新緩存的內(nèi)存屏障指令
- 逃逸分析的結(jié)果更多被用于將新建對(duì)象操作轉(zhuǎn)換成棧上分配或者標(biāo)量替換
- 如果逃逸分析能夠證明某些新建的對(duì)象不逃逸,那么 Java 虛擬機(jī)完全可以將其分配至棧上蝇裤,并且在 new 語句所在的方法退出時(shí)廷支,通過彈出當(dāng)前方法的棧楨來自動(dòng)回收所分配的內(nèi)存空間
- 不過频鉴,由于實(shí)現(xiàn)起來需要更改大量假設(shè)了“對(duì)象只能堆分配”的代碼,因此 HotSpot 虛擬機(jī)并沒有采用棧上分配恋拍,而是使用了標(biāo)量替換這么一項(xiàng)技術(shù)
- 標(biāo)量替換
- 所謂的標(biāo)量垛孔,就是僅能存儲(chǔ)一個(gè)值的變量,比如 Java 代碼中的局部變量
- 與之相反施敢,聚合量則可能同時(shí)存儲(chǔ)多個(gè)值周荐,其中一個(gè)典型的例子便是 Java 對(duì)象
- 標(biāo)量替換這項(xiàng)優(yōu)化技術(shù),可以看成將原本對(duì)對(duì)象的字段的訪問僵娃,替換為一個(gè)個(gè)局部變量的訪問
部分逃逸分析
部分逃逸分析將根據(jù)控制流信息概作,判斷出新建對(duì)象僅在部分分支中逃逸,并且將對(duì)象的新建操作推延至對(duì)象逃逸的分支中悯许。這將使得原本因?qū)ο筇右荻鵁o法避免的新建對(duì)象操作仆嗦,不再出現(xiàn)在只執(zhí)行 if-else 分支的程序路徑之中
- 與 C2 所使用的逃逸分析相比辉阶,Graal 所使用的部分逃逸分析能夠優(yōu)化更多的情況先壕,不過它編譯時(shí)間也更長(zhǎng)一些