前言
在實際中,Java程序中的對象或許 本身就是逃逸 的,或許因為 方法內聯不夠徹底 而被即時編譯器 當成是逃逸 的,這兩種情況都將
導致即時編譯器 無法進行標量替換 ,這時,針對對象字段訪問的優化顯得更為重要。
static int bar(Foo o, int x) { o.a = x; return o.a; }
static int bar(Foo o, int x) { o.a = x; return x; }
即時編譯器也能作出類似的 自動優化
字段讀取優化
即時編譯器會優化 實例字段 和 靜態字段 的訪問,以 減少總的內存訪問次數
即時編譯器將 沿著控制流 ,緩存各個字段 存儲節點 將要存儲的值,或者字段 讀取節點 所得到的值
樣例1
static int bar(Foo o, int x) { int y = o.a + x; return o.a + y; }
實例字段Foo.a被讀取兩次,即時編譯器會將第一次讀取的值緩存起來,并且 替換 第二次的字段讀取操作,以 節省 一次內存訪問
static int bar(Foo o, int x) { int t = o.a; int y = t + x; return t + y; }
樣例2
static int bar(Foo o, int x) { o.a = 1; if (o.a >= 0) return x; else return -x; }
字段讀取節點被替換成一個 常量 ,進一步觸發更多的優化
static int bar(Foo o, int x) { o.a = 1; return x; }
樣例3
class Foo { boolean a; void bar() { a = true; while (a) {} } void whatever() { a = false; } }
即時編譯器會將while循環中讀取實例字段a的操作 直接替換為常量true
void bar() { a = true; while (true) {} } // 生成的機器碼將陷入這一死循環中 0x066b: mov r11,QWORD PTR [r15+0x70] // 安全點測試 0x066f: test DWORD PTR [r11],eax // 安全點測試 0x0672: jmp 0x066b // while (true)
1、可以通過 volatile 關鍵字標記實例字段a,以 強制 對a的讀取
2、實際上,即時編譯器將 在volatile字段訪問前后插入內存屏障節點
3、同理, 加解鎖操作同樣也會阻止即時編譯器的字段讀取優化
字段存儲優化
如果一個字段先后被存儲了兩次,而且這 兩次存儲之間沒有對第一次存儲內容讀取 ,那么即時編譯器將 消除 第一個字段存儲
樣例1
class Foo { int a = 0; void bar() { a = 1; a = 2; } }
即時編譯器將消除bar方法的冗余存儲
void bar() { a = 2; }
樣例2
即便在某個字段的兩個存儲操作之間讀取該字段,即時編譯器也可能在 字段讀取優化 的幫助下,將第一個存儲操作當作 冗余存儲
場景:例如兩個存儲操作之間隔著許多代碼,又或者因為 方法內聯 的原因,將兩個存儲操作納入到同一編譯單元里(如構造器中字段的初始化以及隨后的更新)
class Foo { int a = 0; void bar() { a = 1; int t = a; a = t + 2; } } // 優化為 class Foo { int a = 0; void bar() { a = 1; int t = 1; a = t + 2; } } // 進一步優化為 class Foo { int a = 0; void bar() { a = 3; } }
如果所存儲的字段被標記為 volatile ,那么即時編譯器也 不能消除冗余存儲
死代碼消除
樣例1
int bar(int x, int y) { int t = x*y; t = x+y; return t; }
沒有節點依賴于t的第一個值 x*y ,因此該乘法運算將被消除
int bar(int x, int y) { return x+y; }
樣例2
int bar(boolean f, int x, int y) { int t = x*y; if (f) t = x+y; return t; }
部分程序路徑上有冗余存儲(f=true),該路徑上的乘法運算將會被消除
int bar(boolean f, int x, int y) { int t; if (f) t = x+y; else t = x*y; return t; }
樣例3
int bar(int x) { if (false) return x; else return -x; }
不可達分支指的是任何程序路徑都不可達到的分支,即時編譯器將 消除不可達分支
int bar(int x) { return -x; }
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。