假如您頻繁存取變量,就需要考慮從何處存取這些變量。變量是 static 變量,還是堆棧變量,或者是類的實例變量?變量的存儲位置對存取它的代碼的性能有明顯的影響?例如,請考慮下面這段代碼:
class StackVars
{
private int instVar;
private static int staticVar;
//存取堆棧變量
void stackAccess(int val)
{
int j=0;
for (int i=0; i<val; i++)
j += 1;
}
//存取類的實例變量
void instanceAccess(int val)
{
for (int i=0; i<val; i++)
instVar += 1;
}
//存取類的 static 變量
void staticAccess(int val)
{
for (int i=0; i<val; i++)
staticVar += 1;
}
}
這段代碼中的每個方法都執行相同的循環,並反復相同的次數。唯一的不同是每個循環使一個不同類型的變量遞增。方法 stackAccess 使一個局部堆棧變量遞增,instanceAccess 使類的一個實例變量遞增,而 staticAccess 使類的一個 static 變量遞增。
instanceAccess 和 staticAccess 的執行時間基本相同。但是,stackAccess 要快兩到三倍。存取堆棧變量如此快是因為,JVM 存取堆棧變量比它存取 static 變量或類的實例變量執行的操作少。請看一下為這三個方法生成的字節碼:
Method void stackAccess(int)
0 iconst_0 //將 0 壓入堆棧。
1 istore_2 //彈出 0 並將它存儲在局部分變量表中索引為 2 的位置 (j)。
2 iconst_0 //壓入 0。
3 istore_3 //彈出 0 並將它存儲在局部變量表中索引為 3 的位置 (i)。
4 goto 13 //跳至位置 13。
7 iinc 2 1 //將存儲在索引 2 處的 j 加 1。
10 iinc 3 1 //將存儲在索引 3 處的 i 加 1。
13 iload_3 //壓入索引 3 處的值 (i)。
14 iload_1 //壓入索引 1 處的值 (val)。
15 if_icmplt 7 //彈出 i 和 val。假如 i 小於 val,則跳至位置 7。
18 return //返回調用方法。
Method void instanceAccess(int)
0 iconst_0 //將 0 壓入堆棧。
1 istore_2 //彈出 0 並將它存儲在局部變量表中索引為 2 的位置 (i)。
2 goto 18 //跳至位置 18。
5 aload_0 //壓入索引 0 (this)。
6 dup //復制堆棧頂的值並將它壓入。
7 getfield #19 <Field int instVar>
//彈出 this 對象引用並壓入 instVar 的值。
10 iconst_1 //壓入 1。
11 iadd //彈出棧頂的兩個值,並壓入它們的和。
12 putfield #19 <Field int instVar>
//彈出棧頂的兩個值並將和存儲在 instVar 中。
15 iinc 2 1 //將存儲在索引 2 處的 i 加 1。