【问题标题】:Creating objects inside loop vs. creating one temp object before loop在循环内创建对象与在循环前创建一个临时对象
【发布时间】:2013-11-07 20:30:40
【问题描述】:

这是我要问的一段代码

        for (int i = 0; i < this.options.size(); i++) {
            RadioButton butt = this.options.get(i);
            //do something with butt
        }

如果我将其更改为:

        RadioButton butt;
        for (int i = 0; i < this.options.size(); i++) {
            butt = this.options.get(i);
            //do something with butt
        }

编辑:如果此代码每秒执行 30-50 次,options 的大小约为 20,该怎么办?

【问题讨论】:

  • 巨大?不。唯一的区别是 1 个指针分配 1 个内存,而不是循环的每个项目分配 1 个内存。鉴于这是一个 ui 元素,您最多可能有几十个,这使得它完全可以忽略不计。
  • 当您观察到您的应用程序的性能不符合您的性能要求并分析您的应用程序时,您确定这是瓶颈所在吗?您的测试中有哪些可衡量的差异?
  • @Jason 我还没有测试我的应用程序的性能。我只是想知道哪种方法更快(显然没有太大区别)。无论如何,我在谷歌和 StackOverflow 上搜索但没有找到答案,这就是我发布的原因。
  • 关于您的编辑:您应该更担心为什么需要在 20 项列表上每秒循环 50 次,而不是声明是否应该进入或退出。
  • 回答您的编辑:在这两种情况下再次相同。看我的回答。

标签: java android performance


【解决方案1】:

对于所有现实的、可衡量的案例,两者在性能方面绝对没有区别。事实上,我很确定(诚然我不确定)它们会产生完全相同数量的分配和参考创建。 JVM 创建 N 个引用持有者是愚蠢的。它会简单地重用在第一次迭代中创建的那个,只需在下一次赋值中给它引用。这意味着两种情况都只使用一个参考持有人(假设这是真的)。​​

【讨论】:

  • 我猜由于智能编译器优化,这使其差异为 0。
【解决方案2】:

你不是在这里创建对象,你只是在创建引用,你是创建一个还是多个引用并不重要。

【讨论】:

    【解决方案3】:

    看了标题,我就知道这将是又一个被误导的性能问题。

    有几点:

    • 不,除了变量的范围之外,它们实际上是相同的。
    • 一般来说,如果您担心这样的微优化,那么您就是把时间花在了完全错误的事情上。在这种情况下,它没有实际意义,因为没有区别,但即使你在谈论例如一项任务:
      1. 与您正在执行的其他操作相比,差异仅为纳秒级,完全可以忽略不计。
      2. 编译器在优化方面比您聪明得多。
      3. JVM 解释器和热点编译器也比你聪明得多。
    • 如果您没有设置明确的性能要求,并且您还没有确定您的代码不满足这些要求,并且您还没有分析您的代码并确定瓶颈在哪里,您没有必要提出这样的优化问题。

    至于您在另一个答案中所做的 GC 评论:GC 发生在后台,是智能的,并且做出您完全零控制的决定(除了 JVM 命令行调整 - 不要兴奋,基于事实上,你问了这个问题,你可能没有能力对调整参数做出正确的决定)。将引用从一个地方移动到另一个地方不会让您对 GC 处理它的方式进行重大控制。每次循环前一个引用不再可达,GC 将在未来的未定义点清理它。

    【讨论】:

    • 我认为有区别,因为RadioButton butt 会为引用分配内存,因此将声明放在循环之外会导致分配更少。在实践中,编译器可能足够聪明,可以自己做这件事。而且分配参考槽真的没什么大不了的。
    • 您不能说这会导致“内存分配以供参考”。是在登记簿上吗?在堆栈上?在本地堆栈池中?查看生成的字节码。更重要的是,这重要吗?不要通过暗示即使是最坏的情况也是合理的优化目标来误导 OP。这是一个明显而经典的误导、过早的微优化案例,甚至暗示它有实质内容是对 OP 和任何可能看到这一点的志同道合的读者的伤害。
    • 作为一个新程序员,真的很难知道你做某事是否糟糕,或者是否有很多方法中的一种更好。这是对这个问题的一个很好的答案,如果没有贬低的俏皮话会更好。
    • 我完全同意,除非微优化部分。我只是认为写Object a;会创建一个占用4字节堆的引用。
    • 贬义的俏皮话是出于爱。
    【解决方案4】:

    我认为代码和性能几乎相同,只是看起来不同。您不是在创建新实例,而只是从您的集合中复制对象的引用。

    但我喜欢并且通常使用第二种方法。

    【讨论】:

    • 所以在每个循环之后,该循环中的引用会立即被回收?然后在下一个循环中,我再次引用了一个对象。所以从这个逻辑来看,第二种方法会更快,因为它只是将引用分配给临时引用,即 GC 的工作更少,内存分配更少?
    • GC 发生在后台,是智能的,并且可以做出您完全零控制的决定。将引用从一个地方移动到另一个地方不会让您对 GC 处理它的方式进行重大控制。每次循环之前的引用不再可达,GC 将在未来的未定义点清理它。
    • @nevercode 我会说几乎和你一样,但这也取决于 GB、编译器等的行为,但差异不会很大。
    • @nevercode:你真的担心什么时候会收集一堆参考资料吗?一个引用是 4 个字节。
    • @nevercode 你是认真的吗?对于一个人来说,每秒 30-60 次是爬行。您是否相信任何此类差异的数量级甚至会接近您所说的每帧约 20 毫秒?您在这里所说的几纳秒大约是其分数的 1/10,000,000。其次,您认为数组的大小如何影响这里的任何东西?你知道数组列表是如何工作的吗?
    【解决方案5】:

    差异并不大,因为分配对象是这里最大的成本。此外,编译器将使您的代码更高效,因此最终它的性能成本是相同的。

    【讨论】:

      【解决方案6】:

      在这两种情况下,您都在循环中创建 RadioButton 对象,因为 RadioButton butt 它只是一个引用而不是对象的实例。大概是 this.option.get(i) 创建您的对象。 所以我的回答是:不会。

      唯一改变的是,在第二个循环中,您将创建 this.options.size()-引用 butt 的倍数

      【讨论】:

      • 大概,options 是一个 List,因此 get 检索一个元素,但不创建它。此外,get 很少是用于表示某物已创建的方法名称。
      • 是的,现在明白了。在这种情况下,他仅将先前实例化的对象与新引用链接。所以他根本没有创建一个新对象。
      猜你喜欢
      • 2014-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-04
      相关资源
      最近更新 更多