`
yinwufeng
  • 浏览: 286953 次
  • 性别: 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 执行。

分享到:
评论

相关推荐

    java性能优化java性能优化

    Java性能优化是提升Java应用程序效率的关键技术,涵盖了内存管理、代码优化、I/O处理等多个方面。以下是一些关键的性能优化策略: 1. **对象创建与克隆**:使用`new`关键字创建对象时,会调用构造函数链,这可能...

    Java性能优化技巧集锦

    ### Java性能优化技巧集锦 #### 一、通用篇 **1.1 不用new关键词创建类的实例** - **背景**: 使用`new`关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。这种方法可能会消耗较多资源。 - **优化...

    Java性能优化的45个细节

    4. **使用局部变量**:尽可能减少方法的参数,避免方法间共享对象,提高局部性原理的利用。 5. **避免空对象检查**:如果可能,使用Optional类来封装可能为null的对象,减少显式的null检查。 6. **使用equals()与...

    java系统性能优化手册

    1. **字段值的获取**:从 `ResultSet` 获取字段值时,应尽可能使用与字段类型相匹配的获取方法,避免类型转换带来的额外开销。 2. **字段选择**:只检索必要的字段,避免使用 `SELECT * FROM tableName` 类型的语句...

    Java性能优化

    在Java性能优化中,合理使用单例模式可以带来以下优势: 1. **控制资源使用**:通过线程同步来控制资源的并发访问,尤其是在多线程环境下,可以有效减少资源竞争,提高资源利用率。 2. **控制实例产生**:减少不必...

    java性能优化方法

    通过以上分析,我们可以看到Java性能优化不仅涉及到集合操作和反射机制的巧妙运用,还涵盖了对象创建的优化和基础编程技巧。对于有经验的Java开发者来说,深入理解并实践这些方法,将显著提升代码质量和系统性能。在...

    2022年Java性能优化技巧集锦Java教程.docx

    以下是一些2022年仍然适用的Java性能优化技巧: 1. **避免使用`new`关键字创建对象** 使用`new`创建对象时,会执行构造函数链,消耗额外的时间。如果对象实现了`Cloneable`接口,可以考虑使用`clone()`方法,它不...

    java性能优化.pdf

    下面将详细讨论一些通用的Java性能优化技巧。 1.1 避免使用`new`关键字创建对象 创建对象时,构造函数链中的所有构造函数都会被调用,这可能导致不必要的开销。如果对象实现了`Cloneable`接口,可以使用`clone()`...

    java 性能优化

    ### Java性能优化技巧详解 #### 一、通用篇:适用多数Java应用的优化策略 **1.1 使用`clone()`方法代替`new`** 在Java中,通过`new`关键字创建对象实例时,会自动调用类的所有构造函数,这可能会带来额外的开销。...

    java性能优化借鉴

    ### Java性能优化借鉴 #### 一、合理使用单例模式 单例模式是软件开发中常用的模式之一,它确保一个类只有一个实例,并提供一个全局访问点。在Java中使用单例模式可以有效控制资源的使用,减少不必要的实例化操作...

    Java性能优化演示稿

    Java性能优化是提升应用程序效率的关键,特别是在开发高性能网站时,每一点优化都能带来显著的性能提升。以下是一些Java性能优化的关键点: 1. **合理分配对象空间**:在Java中,很多类如`Vector`有默认的内存分配...

    44条Java代码优化建议

    尽可能地使用局部变量,因为局部变量存储在栈中,相对于堆内存中的对象来说,访问速度更快。而且局部变量不需要垃圾回收器来回收,能够减轻垃圾回收的压力。 4. 及时关闭流 在进行数据库连接、文件IO等操作时,应当...

    java时间空间性能优化附带个人测试代码

    本篇将基于《java time and space performance tips》一书中的知识,结合提供的个人测试代码,深入探讨Java性能优化的策略。 1. **避免不必要的对象创建** - 在Java中,频繁的对象创建和垃圾回收会增加运行时的...

    java代码性能的优化

    栈中的变量访问速度快于堆中的对象,因此尽可能使用局部变量而非对象实例可以提高程序执行效率。 #### 6. 装箱与拆箱 装箱是指将基本数据类型转换为对应的包装类,而拆箱则是相反的过程。频繁地进行装箱和拆箱操作...

    Java程序性能优化之二十三个建议

    8. **使用局部变量**:尽可能将变量声明为局部变量,减少作用域,提高效率。 9. **谨慎使用反射**:反射虽然灵活,但性能开销大,尽量避免在性能敏感区域使用。 10. **避免在循环中调用方法**:如果可能,将方法...

Global site tag (gtag.js) - Google Analytics