编写高效代码并优化内存

时间:2011-06-15 07:30:57

标签: java garbage-collection coding-style

我有两个代码片段(用Java编写)更有效率吗?

int foo(){

   int result;
   for(int i = 0; i < n;i++){
      SomeObject a,b,c;
      a = new SomeObject();
      b = new SomeObject();
      c = new SomeObject();
      //do something with a,b,c
      //and derive result
   }
   return result;
}

int foo(){

   int result;
   SomeObject a,b,c;
   a = new SomeObject();
   b = new SomeObject();
   c = new SomeObject();

   for(int i = 0; i < n;i++){
      a.flush(); //reset object do not create new though
      b.flush(); //reset object do not create new though
      c.flush(); //reset object do not create new though
      //do something with a,b,c
      //and derive result
   }
   return result;
}

在第二个片段中,我已将局部变量移出循环,因此实际上它只创建了一个实例。这会改善什么吗? 从逻辑上讲,当变量在循环内部时,它是有意义的。但是垃圾收集器会有效地清理对象吗?

编辑:更新了有关对象实例化的剪切。

5 个答案:

答案 0 :(得分:4)

您已将变量移出,但那些不是对象。假设您在循环的每次迭代中为abc分配了不同的值,您根本没有减少内存分配 - 您只是扩大了范围变量不必要。

如果您不需要在循环中更改abc的值,那么这是另一回事,分配它们会更好一点在循环之外而不是每次迭代。

主要的是要实现对象变量之间的区别。特别是,在循环的每次迭代中创建“新”变量实际上不会花费任何成本。

编辑:好的,有了更新的问题,显然 在创建的对象数方面存在差异。但是,取决于第二种形式的确切内容可能仍然更为可取:它当然更容易理解,并且它不依赖于flush操作的细节。另一方面,如果创建SomeObject的实例是昂贵的(例如,它生成加密密钥),那么优化它是一个好主意。

简而言之:取决于此。首先编写最易读的代码,对其进行测量,然后将其与性能要求进行比较。只有在有明显的好处时,才会转向不太明显的代码。

答案 1 :(得分:3)

如上所述,它们是一样的。在这两种情况下,当函数开始执行时,SomeObject引用的内存在函数的堆栈帧中分配。

因此,更喜欢范围最有限的版本。这也有另一个好处 - 如果你的函数是一个并且做了一些在循环之后需要一些时间的事情,那么SomeObject实例将有资格立即循环结束GC。

但请注意,如果使用new 初始化一个或多个对象,则会产生重大差异。构建的对象相同每次循环迭代 它都是可重用的。那么在每次循环迭代中避免不必要地创建和丢弃相同的对象会好得多。

编辑更新的问题

FWIW,我不同意Jon Skeet关于这个细节:鉴于更新的问题,在循环之外构造对象并在每次迭代时重用它们总是更可取的。如果方法中的其他代码非常复杂以至于不合需要,那么需要重构的是方法,而不是循环。

答案 2 :(得分:1)

垃圾收集确实有效地清理对象,但这并不意味着它是零成本。你需要在第二个例子中考虑的是你必须有可变对象,这些对象可以重置()回到它们的原始状态,这也不是零成本。

大多数表现问题的答案是;尽可能简单地编写代码并且结果也将具有良好的性能,这通常就足够了。如果确实存在性能问题,请对应用程序进行概要分析,以了解可能需要更改的内容。

答案 3 :(得分:1)

我实际上已经对此进行了测试,并且没有区别,因为编译器发现选项1可以更好地完成选项2并将其更改为无论如何。

代码为清晰起见,效率第二(编译器比你更聪明)

答案 4 :(得分:0)

这取决于,如果他们在两个实例中构建每个循环迭代,那么两者都不是更有效。如果你在循环体外面初始化它们,那么后者会更快。