使用臨時變量提高Java代碼性能

一個Java類中可以存在多種形式的變量,可以是最一般的成員變量;或將其定義爲靜態變量;也可以在方法中定義臨時變量。這三種變量的存取效率如何?
1.測試
下面作個實驗,看下面代碼。
class CheckVars {
    privateintinstVar;// 成員變量
    privatestaticintstaticVar// 靜態變量
 
    // 存取類方法中的臨時變量
    void tempAccess(int val) {
       int j = 0;// 臨時變量
       long startTime = System.currentTimeMillis();
       for (int i = 0; i < val; i++)
           j += 1;
       long endTime = System.currentTimeMillis();
       System.out.println("temp var: " + (endTime - startTime) + " milli seconds");
    }
 
    // 存取類的成員變量
    void instanceAccess(int val) {
       long startTime = System.currentTimeMillis();
       for (int i = 0; i < val; i++)
           instVar += 1;
       long endTime = System.currentTimeMillis();
       System.out.println("instance var: " + (endTime - startTime) + " milli seconds");
    }
 
    // 存取類的 static 變量
    void staticAccess(int val) {
       long startTime = System.currentTimeMillis();
       for (int i = 0; i < val; i++)
           staticVar += 1;
       long endTime = System.currentTimeMillis();
       System.out.println("static var: " + (endTime - startTime) + " milli seconds");
    }
   
    publicstaticvoid main(String[] args){
       CheckVars test=new CheckVars();
       test.tempAccess(200000000);
       test.instanceAccess(200000000);
       test.staticAccess(200000000);
    }
}
 
結果:
temp var: 350 milli seconds
instance var: 821 milli seconds
static var: 852 milli seconds
 
這段代碼中的每個方法都執行相同的循環並反覆相同的次數。唯一的不同是每個循環使一個不同類型的變量遞增。方法 tempAccess 使一個局部堆棧變量遞增,instanceAccess 使類的一個成員實例變量遞增,而 staticAccess 使類的一個 static 變量遞增。
從結果中可以發現,instanceAccess  staticAccess 的執行時間基本相同。但是,tempAccess 要快兩到三倍。
 
2JVM存取變量機制
存取堆棧變量如此快是因爲JVM 存取堆棧變量比它存取 static 變量或類的實例變量執行的操作少。
JVM 是一種基於堆棧的虛擬機,因此優化了對堆棧數據的存取和處理。所有局部變量都存儲在一個局部變量表中,在 Java 操作數堆棧中進行處理,並可被高效地存取。
存取 static 變量和實例變量成本更高,因爲 JVM 必須使用代價更高的操作碼,並從常數存儲池中存取它們。(常數存儲池保存一個類型所使用的所有類型、字段和方法的符號引用。)通常,在第一次從常數存儲池中訪問 static 變量或實例變量以後,JVM 將動態更改字節碼以使用效率更高的操作碼。儘管有這種優化,堆棧變量的存取仍然更快。
 
3.優化代碼
考慮到這些事實,就可以重新構建前面的代碼,以便通過存取堆棧變量而不是實例變量或 static 變量使操作更高效。
publicclass CheckVarsAdv {
    privateintinstVar;
    privatestaticintstaticVar;
 
    void tempAccess(int val) {
       int j = 0;
       long startTime = System.currentTimeMillis();
       for (int i = 0; i < val; i++)
           j += 1;
       long endTime = System.currentTimeMillis();
       System.out.println("temp var: " + (endTime - startTime) + " milli seconds");
    }
 
    void instanceAccess(int val) {
       int j = instVar;
       long startTime = System.currentTimeMillis();
       for (int i = 0; i < val; i++)
           j += 1;
       long endTime = System.currentTimeMillis();
       System.out.println("instance var: " + (endTime - startTime) + " milli seconds");
       instVar = j;
    }
 
    void staticAccess(int val) {
       int j = staticVar;
       long startTime = System.currentTimeMillis();
       for (int i = 0; i < val; i++)
           j += 1;
       long endTime = System.currentTimeMillis();
       System.out.println("static var: " + (endTime - startTime) + " milli seconds");
       staticVar = j;
    }
   
    publicstaticvoid main(String[] args){
       CheckVarsAdv test=new CheckVarsAdv();
       test.tempAccess(200000000);
       test.instanceAccess(200000000);
       test.staticAccess(200000000);
    }
}
 
結果:
temp var: 341 milli seconds
instance var: 370 milli seconds
static var: 361 milli seconds
 
方法 instanceAccess  staticAccess 被修改爲將它們的實例變量或 static 變量複製到局部堆棧變量中。當變量的處理完成以後,其值又被複制回實例變量或 static 變量中。這種簡單的更改明顯提高了 instanceAccess  staticAccess 的性能。這三個方法的執行時間現在基本相同。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章