`
yinwufeng
  • 浏览: 277725 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java性能优化技巧之尽可能使用局部变量

    博客分类:
  • java
 
阅读更多

 


如果你频繁存取变量,就需要考虑从何处存取这些变量。变量是 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。

  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>

                      //将常数存储池中 staticvar 的值压入堆栈。

   8 iconst_1         //压入 1。

   9 iadd             //弹出栈顶的两个值,并压入他们的和。

  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           //返回调用方法。


查看字节码揭示了堆栈变量效率更高的原因。jvm 是一种基于堆栈的虚拟机,因此优化了对堆栈数据的存取和处理。所有局部变量都存储在一个局部变量表中,在 java 操作数堆栈中进行处理,并可被高效地存取。存取 static 变量和实例变量成本更高,因为 jvm 必须使用代价更高的操作码,并从常数存储池中存取他们。(常数存储池保存一个类型所使用的所有类型、字段和方法的符号引用。)

通常,在第一次从常数存储池中访问 static 变量或实例变量以后,jvm 将动态更改字节码以使用效率更高的操作码。尽管有这种优化,堆栈变量的存取仍然更快。

考虑到这些事实,就能重新构建前面的代码,以便通过存取堆栈变量而不是实例变量或 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 的性能。这三个方法的执行时间目前基本相同,instanceaccess 和 staticaccess 的执行速度只比 stackaccess 的执行速度慢大约 4%。


这并不表示你应该避免使用 static 变量或实例变量。你应该使用对你的设计有意义的存储机制。例如,如果你在一个循环中存取 static 变量或实例变量,则你能临时将他们存储在一个局部堆栈变量中,这样就能明显地提高代码的性能。这将提供最高效的字节码指令序列供 jvm 执行。

分享到:
评论

相关推荐

    75.java成员变量与局部变量.zip

    75.java成员变量与局部变量.zip75.java成员变量与局部变量.zip75.java成员变量与局部变量.zip75.java成员变量与局部变量.zip75.java成员变量与局部变量.zip75.java成员变量与局部变量.zip75.java成员变量与局部变量....

    Java 成员变量和局部变量

    Java 面向对象中的两类 变量 : 成员变量和局部变量

    Java性能优化技巧集锦

    Java性能优化技巧集锦 很全 。。。  1.1 不用new关键词创建类的实例  1.2 使用非阻塞I/O  1.3 慎用异常  1.4 不要重复初始化变量  1.5 尽量指定类的final修饰符  1.6 尽量使用局部变量  1.7 乘法和除法 二、...

    Java性能优化的45个细节.pdf

    Java性能优化的45个细节 1.尽量在合适的场合使用单例 2.尽量避免随意使用静态变量 ...

    全局变量、局部变量、静态全局变量、静态局部变量的区别

    全局变量、局部变量、静态全局变量、静态局部变量的区别

    成员变量和局部变量

    局部变量:在方法内或者方法声明处 在内存中的位置不同 成员变量:在堆内存中 局部变量:在栈内存中 初始化值不同 成员变量:有默认的初始化值 局部变量:没有初始化值,必须手动初始化 生命周期不同 成员...

    JAVA最佳性能优化.pptx

    JAVA最佳性能优 化JAVA优化技巧 类的方法内联,方法的同步及局部变量的使用 创建类的实例优化策略 建立对象池提升性能策略 JAVA垃圾回收机制及引用优化策略

    局部变量 与 全局变量

    通过代码的运行,并理解代码,明白C中的局部变量与本地变量的区别

    Java性能优化

    Java性能优化: 1.尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面: 控制资源的使用,通过线程同步...

    如何获得局部变量名

    如何获得局部变量名 关于System.out.println("abc="+abc);的优化 尽管eclipse提供了Debug工具,还是习惯写System.out.println("abc="+abc); 经常写这样的语句,总感觉变量abc每次写两遍,有些重复。 网上找了很多资料...

    java局部变量.txt

    java局部变量

    VB 局部变量举例

    VB 局部变量举例 VB 局部变量举例 VB 局部变量举例

    在JavaScript中,为什么要尽可能使用局部变量?

    在JavaScript中,我们应该尽可能的用局部变量来代替全局变量,这句话所有人都知道,可是这句话是谁先说的?为什么要这么做?有什么根据么?

    静态全局变量,静态局部变量,全局变量,局部变量

    静态全局变量,静态局部变量,全局变量,局部变量静态全局变量,静态局部变量,全局变量,局部变量

    [面试/笔试系列3]局部变量能否和全局变量重名

    1、局部变量能否和全局变量重名? 答:能,局部会屏蔽全局。要用全局变量,需要使用"::" ; 局部变量可以与全局变量同名, 在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器 而言...

    Java优化编程(第2版)

    Java优化编程(第2版)通过丰富、完整、富有代表性的实例,展示了如何提升Java应用性能,并且给出了优化前与优化后的Java应用程序的性能差别,以实际的实例与数字告诉你,为什么不可以这么做,应该怎么做,深入分析...

    ios-block的局部变量和全局变量和static的静态变量联系.zip

    block,局部变量,全部变量,static的静态变量关系

    全局变量、静态全局变量、静态局部变量和局部变量的区别2.pdf

    变量可以分为全局变量、静态全局变量、静态局部变量和局部变量 按存储区域分:全局变量、静态全局变量和静态局部变量都存放在内存的全局数据区,局部变量存放在内存的栈区 按作用域分:全局变量在整个工程文件内都...

    浅谈java中的局部变量和全局变量

    主要涉及了java中的局部变量和全局变量,就二者的含义、生存时间和创建位置作了介绍,需要的朋友可以参考下。

    Java实例变量、类变量、局部变量

    类变量是类中的静态变量,是用static修饰的变量;实例变量就是类中的成员变量,没有用static修饰的。 类变量是所有对象公用的,实例变量是对象私有的;当一个对象将类变量进行修改...局部变量,即定义在方法内部的变量。

Global site tag (gtag.js) - Google Analytics