在啥时候定义变量比进行方法调用更有效?
Posted
技术标签:
【中文标题】在啥时候定义变量比进行方法调用更有效?【英文标题】:At what point does defining a variable become more efficient than making method calls?在什么时候定义变量比进行方法调用更有效? 【发布时间】:2013-03-14 11:52:03 【问题描述】:我正在阅读Google's tips on android optimisation,他们提出的第一点之一是您应该避免使用短期变量,因为这意味着更多的垃圾收集。 那么,有没有一点你会更好地定义一个变量并使用它而不是每次你想使用该对象时调用一个访问器?说,这个:
for(int i = 0; i < 1000; i++)
foo.getBar() // then do something with it
与此相反:
Bar bar = foo.getBar();
for(int i = 0; i < 1000; i++)
bar // then do something with it
在性能方面有什么区别?
【问题讨论】:
关于垃圾收集的要点与类级变量有关。如果您想知道将 bar 声明为类级别变量还是方法局部变量更好,那么这取决于您访问它的频率及其生命周期。在一个方法中,第二个选项当然更好。 【参考方案1】:从您提供的链接中:“您应该避免创建不需要的对象实例”
-
对象实例!= 短期变量
不要忘记句子的第二部分...
局部变量通常是allocated on the stack(其中 GC 无关紧要),而新的对象实例将在堆上分配 - 这是完全不同的故事。 TMHO 说“如果你真的不需要,就不要做某事”这样的话没有什么价值。此外,尽可能避免创建局部变量可能会对您的代码产生非常糟糕的影响,read this
【讨论】:
【参考方案2】:假设没有编译器优化
当你使用
for(int i = 0; i < 1000; i++)
foo.getBar() // then do something with it
您的代码将转到foo
引用的实例,执行其getBar()
方法,然后该方法将返回它应该返回的任何实例/值。这将针对该循环的 1000 次执行中的每一次执行。每次执行完成后,getBar()
返回的值都必须进行垃圾回收。
但是,当您使用时:
Bar bar = foo.getBar();
for(int i = 0; i < 1000; i++)
bar // then do something with it
您只获得一次foo.getBar()
的值,在您的循环之外并在本地存储它的副本。然后在循环中使用此本地副本 1000 次。
第二个选项效率更高,因为您的foo
实例的getBar()
方法只被调用一次,而不是一千次。这意味着您不会将 getBar()
方法中的代码执行 1000 次,这显然比执行一次更糟糕。
这些都是真正的微优化。除非你在getBar()
中做一些真正的繁重工作,否则这不会产生明显的影响。在很多情况下,编译器优化会将这两者转换为完全相同的字节码,因此您无论如何都不必担心。
【讨论】:
我认为编译器为了优化而将方法调用移到循环外是没有意义的。 @BheshGurung 实际上确实如此:每当您调用一个方法(假设它返回相同的结果,例如,最终变量的值或对静态对象的引用)时,都会创建一个新框架具有“上下文切换”成本的堆栈,即使这个成本很小,最好尽可能避免它。 @alfasin:但是编译器怎么知道呢? AFAIK,编译器不关心方法返回的值是否是编译时常量。我想如果我们可以在方法返回类型前面添加final
关键字是可能的:)。
@BheshGurung 在我看来,为返回常量值(最终/引用)的方法实现编译器优化应该相当容易,但我可能错了 :)【参考方案3】:
在这个例子中,假设 bar 只是一个由 getBar 返回的对象并且没有被创建,就 GC 而言没有区别。
但是,方法调用的开销也很小,您的第二个示例会更有效。
但是,如果 getBar 在每次调用时创建一个新对象,则将创建 1000 个新 Bar,然后在第二个示例中进行 GC,而第一个保持不变。
【讨论】:
以上是关于在啥时候定义变量比进行方法调用更有效?的主要内容,如果未能解决你的问题,请参考以下文章
在啥情况下 do-while 可以比 while 更有效率?