前言
在實(shí)際中,java程序中的對(duì)象或許 本身就是逃逸 的,或許因?yàn)?方法內(nèi)聯(lián)不夠徹底 而被即時(shí)編譯器 當(dāng)成是逃逸 的,這兩種情況都將
導(dǎo)致即時(shí)編譯器 無(wú)法進(jìn)行標(biāo)量替換 ,這時(shí),針對(duì)對(duì)象字段訪(fǎng)問(wèn)的優(yōu)化顯得更為重要。
1
2
3
4
|
static int bar(foo o, int x) { o.a = x; return o.a; } |
- 對(duì)象o是傳入?yún)?shù), 不屬于逃逸分析的范圍 (jvm中的逃逸分析針對(duì)的是 新建對(duì)象 )
- 該方法會(huì)將所傳入的int型參數(shù)x的值存儲(chǔ)至實(shí)例字段foo.a中,然后再讀取并返回同一字段的值
- 這段代碼涉及 兩次 內(nèi)存訪(fǎng)問(wèn)操作:存儲(chǔ)和讀取實(shí)例字段foo.a
- 代碼可以手工優(yōu)化成如下
1
2
3
4
|
static int bar(foo o, int x) { o.a = x; return x; } |
即時(shí)編譯器也能作出類(lèi)似的 自動(dòng)優(yōu)化
字段讀取優(yōu)化
即時(shí)編譯器會(huì)優(yōu)化 實(shí)例字段 和 靜態(tài)字段 的訪(fǎng)問(wèn),以 減少總的內(nèi)存訪(fǎng)問(wèn)次數(shù)
即時(shí)編譯器將 沿著控制流 ,緩存各個(gè)字段 存儲(chǔ)節(jié)點(diǎn) 將要存儲(chǔ)的值,或者字段 讀取節(jié)點(diǎn) 所得到的值
- 當(dāng)即時(shí)編譯器 遇到對(duì)同一字段的讀取節(jié)點(diǎn) 時(shí),如果緩存值還沒(méi)有失效,那么將讀取節(jié)點(diǎn) 替換 為該緩存值
-
當(dāng)即時(shí)編譯器 遇到對(duì)同一字段的存儲(chǔ)節(jié)點(diǎn) 時(shí),會(huì) 更新 所緩存的值
- 當(dāng)即時(shí)編譯器遇到 可能更新 字段的節(jié)點(diǎn)時(shí),它會(huì)采取 保守 的策略, 舍棄所有的緩存值
- 方法調(diào)用節(jié)點(diǎn) :在即時(shí)編譯器看來(lái),方法調(diào)用會(huì)執(zhí)行 未知代碼
- 內(nèi)存屏障節(jié)點(diǎn) :其他線(xiàn)程可能異步更新了字段
樣例1
1
2
3
4
|
static int bar(foo o, int x) { int y = o.a + x; return o.a + y; } |
實(shí)例字段foo.a被讀取兩次,即時(shí)編譯器會(huì)將第一次讀取的值緩存起來(lái),并且 替換 第二次的字段讀取操作,以 節(jié)省 一次內(nèi)存訪(fǎng)問(wèn)
1
2
3
4
5
|
static int bar(foo o, int x) { int t = o.a; int y = t + x; return t + y; } |
樣例2
1
2
3
4
5
6
7
|
static int bar(foo o, int x) { o.a = 1 ; if (o.a >= 0 ) return x; else return -x; } |
字段讀取節(jié)點(diǎn)被替換成一個(gè) 常量 ,進(jìn)一步觸發(fā)更多的優(yōu)化
1
2
3
4
|
static int bar(foo o, int x) { o.a = 1 ; return x; } |
樣例3
1
2
3
4
5
6
7
8
|
class foo { boolean a; void bar() { a = true ; while (a) {} } void whatever() { a = false ; } } |
即時(shí)編譯器會(huì)將while循環(huán)中讀取實(shí)例字段a的操作 直接替換為常量true
1
2
3
4
5
6
7
8
|
void bar() { a = true ; while ( true ) {} } // 生成的機(jī)器碼將陷入這一死循環(huán)中 0x066b : mov r11,qword ptr [r15+ 0x70 ] // 安全點(diǎn)測(cè)試 0x066f : test dword ptr [r11],eax // 安全點(diǎn)測(cè)試 0x0672 : jmp 0x066b // while (true) |
1、可以通過(guò) volatile 關(guān)鍵字標(biāo)記實(shí)例字段a,以 強(qiáng)制 對(duì)a的讀取
2、實(shí)際上,即時(shí)編譯器將 在volatile字段訪(fǎng)問(wèn)前后插入內(nèi)存屏障節(jié)點(diǎn)
- 這些 內(nèi)存屏障節(jié)點(diǎn) 將 阻止 即時(shí)編譯器 將屏障之前所緩存的值用于屏障之后的讀取節(jié)點(diǎn)之上
-
在x86_64平臺(tái)上,volatile字段讀取前后的內(nèi)存屏障都是no-op
- 在 即時(shí)編譯過(guò)程中的屏障節(jié)點(diǎn) ,還是會(huì) 阻止即時(shí)編譯器的字段讀取優(yōu)化
- 強(qiáng)制在循環(huán)中使用 內(nèi)存讀取指令 訪(fǎng)問(wèn)實(shí)例字段foo.a的最新值
3、同理, 加解鎖操作同樣也會(huì)阻止即時(shí)編譯器的字段讀取優(yōu)化
字段存儲(chǔ)優(yōu)化
如果一個(gè)字段先后被存儲(chǔ)了兩次,而且這 兩次存儲(chǔ)之間沒(méi)有對(duì)第一次存儲(chǔ)內(nèi)容讀取 ,那么即時(shí)編譯器將 消除 第一個(gè)字段存儲(chǔ)
樣例1
1
2
3
4
5
6
7
|
class foo { int a = 0 ; void bar() { a = 1 ; a = 2 ; } } |
即時(shí)編譯器將消除bar方法的冗余存儲(chǔ)
1
2
3
|
void bar() { a = 2 ; } |
樣例2
即便在某個(gè)字段的兩個(gè)存儲(chǔ)操作之間讀取該字段,即時(shí)編譯器也可能在 字段讀取優(yōu)化 的幫助下,將第一個(gè)存儲(chǔ)操作當(dāng)作 冗余存儲(chǔ)
場(chǎng)景:例如兩個(gè)存儲(chǔ)操作之間隔著許多代碼,又或者因?yàn)?方法內(nèi)聯(lián) 的原因,將兩個(gè)存儲(chǔ)操作納入到同一編譯單元里(如構(gòu)造器中字段的初始化以及隨后的更新)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class foo { int a = 0 ; void bar() { a = 1 ; int t = a; a = t + 2 ; } } // 優(yōu)化為 class foo { int a = 0 ; void bar() { a = 1 ; int t = 1 ; a = t + 2 ; } } // 進(jìn)一步優(yōu)化為 class foo { int a = 0 ; void bar() { a = 3 ; } } |
如果所存儲(chǔ)的字段被標(biāo)記為 volatile ,那么即時(shí)編譯器也 不能消除冗余存儲(chǔ)
死代碼消除
樣例1
1
2
3
4
5
|
int bar( int x, int y) { int t = x*y; t = x+y; return t; } |
沒(méi)有節(jié)點(diǎn)依賴(lài)于t的第一個(gè)值 x*y ,因此該乘法運(yùn)算將被消除
1
2
3
|
int bar( int x, int y) { return x+y; } |
樣例2
1
2
3
4
5
6
|
int bar( boolean f, int x, int y) { int t = x*y; if (f) t = x+y; return t; } |
部分程序路徑上有冗余存儲(chǔ)(f=true),該路徑上的乘法運(yùn)算將會(huì)被消除
1
2
3
4
5
6
7
8
|
int bar( boolean f, int x, int y) { int t; if (f) t = x+y; else t = x*y; return t; } |
樣例3
1
2
3
4
5
6
|
int bar( int x) { if ( false ) return x; else return -x; } |
不可達(dá)分支指的是任何程序路徑都不可達(dá)到的分支,即時(shí)編譯器將 消除不可達(dá)分支
1
2
3
|
int bar( int x) { return -x; } |
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。
原文鏈接:http://zhongmingmao.me/2019/01/06/jvm-advanced-optimization-filed-access/