iload/istore 等,零解析開銷,速度最快。getfield/putfield,經過常量池符號引用解析。getstatic/putstatic,同樣涉及常量池解析。實測證實:在循環中累加操作,棧變量版本比實例/靜態欄位版本快 2~3 倍。
private int x;
private static int staticX;
public void instanceAccess(int val) {
for (int i = 0; i < val; i++) x += 1; // 每次迭代訪問堆
}
public void staticAccess(int val) {
for (int i = 0; i < val; i++) staticX += 1; // 每次迭代訪問方法區
}
public void instanceAccess(int val) {
int tempX = x; // 1 次讀取實例欄位
for (int i = 0; i < val; i++) tempX += 1; // 棧上累加
x = tempX; // 1 次寫回實例欄位
}
public void staticAccess(int val) {
int tempStaticX = staticX;
for (int i = 0; i < val; i++) tempStaticX += 1;
staticX = tempStaticX;
}
| 版本 | 循環體指令 | 指令數 |
|---|---|---|
| 原版 | aload_0 / getfield / iconst_1 / iadd / putfield | 5 |
| 優化版 | iinc(單指令自增局部變量) | 1 |
JIT 編譯器甚至可將 iinc 循環進一步優化為計數器消除,若循環體無副作用可能直接折疊為乘法。
volatile、AtomicInteger 或鎖。JDK 6u23+ 啟用逃逸分析(-XX:+DoEscapeAnalysis),JIT 能自動將未逃逸的對象分配在棧上,並將其欄位拆解為局部變量(標量替換),從而在無需手工修改代碼的情況下獲得同等效果。但對於已發布到堆的長壽命對象,手工提取局部變量仍是必要手段。