Sung in Blog

                     一些技術文章 & 一些生活雜碎
          如果您頻繁存取變量,就需要考慮從何處存取這些變量。變量是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;
            }
            }


          這段代碼中的每個方法都執(zhí)行相同的循環(huán),并反復相同的次數(shù)。唯一的不同是每個循環(huán)使一個不同類型的變量遞增。方法stackAccess使一個局部堆棧變量遞增,instanceAccess使類的一個實例變量遞增,而 staticAccess 使類的一個 static 變量遞增。

          instanceAccess和staticAccess的執(zhí)行時間基本相同。但是,stackAccess要快兩到三倍。存取堆棧變量如此快是因為,JVM存取堆棧變量比它存取static變量或類的實例變量執(zhí)行的操作少。請看一下為這三個方法生成的字節(jié)碼:

          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              //復制堆棧頂?shù)闹挡⑺鼔喝搿?
            7 getfield #19 <Field int instVar>
            //彈出 this 對象引用并壓入 instVar 的值。
            10 iconst_1         //壓入 1。
            11 iadd             //彈出棧頂?shù)膬蓚€值,并壓入它們的和。
            12 putfield #19 <Field int instVar>
            //彈出棧頂?shù)膬蓚€值并將和存儲在 instVar 中。
            15 iinc 2 1         //將存儲在索引 2 處的 i 加 1。
            18 iload_2          //壓入索引 2 處的值 (i)。
            19 iload_1          //壓入索引 1 處的值 (val)。
            20 if_icmplt 5      //彈出 i 和 val。如果 i 小于 val,則跳至位置 5。
            23 return           //返回調用方法。
           
            
             Method void staticAccess(int)
            0 iconst_0         //將 0 壓入堆棧。
            1 istore_2         //彈出 0 并將它存儲在局部變量表中索引為 2 的位置 (i)。
            2 goto 16          //跳至位置 16。
            5 getstatic #25 <Field int staticVar>
            //將常數(shù)存儲池中 staticVar 的值壓入堆棧。
            8 iconst_1         //壓入 1。
            9 iadd             //彈出棧頂?shù)膬蓚€值,并壓入它們的和。
            10 putstatic #25 <Field int staticVar>
            //彈出和的值并將它存儲在 staticVar 中。
            13 iinc 2 1         //將存儲在索引 2 處的 i 加 1。
            16 iload_2          //壓入索引 2 處的值 (i)。
            17 iload_1          //壓入索引 1 處的值 (val)。
            18 if_icmplt 5      //彈出 i 和 val。如果 i 小于 val,則跳至位置 5。
            21 return           //返回調用方法。


          查看字節(jié)碼揭示了堆棧變量效率更高的原因。JVM是一種基于堆棧的虛擬機,因此優(yōu)化了對堆棧數(shù)據(jù)的存取和處理。所有局部變量都存儲在一個局部變量表中,在Java操作數(shù)堆棧中進行處理,并可被高效地存取。存取static變量和實例變量成本更高,因為JVM必須使用代價更高的操作碼,并從常數(shù)存儲池中存取它們。(常數(shù)存儲池保存一個類型所使用的所有類型、字段和方法的符號引用。)

          通常,在第一次從常數(shù)存儲池中訪問static變量或實例變量以后,JVM將動態(tài)更改字節(jié)碼以使用效率更高的操作碼。盡管有這種優(yōu)化,堆棧變量的存取仍然更快。考慮到這些事實,就可以重新構建前面的代碼,以便通過存取堆棧變量而不是實例變量或 static 變量使操作更高效。請考慮修改后的代碼:

            class StackVars
            {
            //與前面相同...
            void instanceAccess(int val)
            {
            int j = instVar;
            for (int i=0; i<val; i++)
            j += 1;
            instVar = j;
            }  
            
            void staticAccess(int val)
            {
            int j = staticVar;
            for (int i=0; i<val; i++)
            j += 1;
            staticVar = j;
            }
            }


          方法 instanceAccess 和 staticAccess 被修改為將它們的實例變量或 static 變量復制到局部堆棧變量中。當變量的處理完成以后,其值又被復制回實例變量或 static 變量中。這種簡單的更改明顯提高了 instanceAccess 和 staticAccess 的性能。這三個方法的執(zhí)行時間現(xiàn)在基本相同,instanceAccess 和 staticAccess 的執(zhí)行速度只比 stackAccess 的執(zhí)行速度慢大約 4%。

          這并不表示您應該避免使用 static 變量或實例變量。您應該使用對您的設計有意義的存儲機制。例如,如果您在一個循環(huán)中存取 static 變量或實例變量,則您可以臨時將它們存儲在一個局部堆棧變量中,這樣就可以明顯地提高代碼的性能。這將提供最高效的字節(jié)碼指令序列供 JVM 執(zhí)行。
          posted on 2005-11-04 15:03 Sung 閱讀(300) 評論(0)  編輯  收藏 所屬分類: Java
          主站蜘蛛池模板: 邵东县| 杭州市| 棋牌| 措勤县| 启东市| 合阳县| 铁岭县| 忻城县| 巍山| 扎赉特旗| 鄯善县| 镇安县| 江达县| 和硕县| 贡嘎县| 宝兴县| 滨州市| 汤阴县| 东丰县| 定州市| 新和县| 台山市| 大庆市| 宽甸| 平罗县| 聂拉木县| 玉环县| 离岛区| 赤水市| 穆棱市| 余庆县| 饶河县| 巧家县| 满洲里市| 泸州市| 中西区| 额尔古纳市| 和林格尔县| 天门市| 南澳县| 鄢陵县|