【问题标题】:Calling getters on an object vs. storing it as a local variable (memory footprint, performance)在对象上调用 getter 与将其存储为局部变量(内存占用、性能)
【发布时间】:2013-10-18 09:07:56
【问题描述】:

在下面的代码中,我们调用listType.getDescription() 两次:

for (ListType listType: this.listTypeManager.getSelectableListTypes())
{
    if (listType.getDescription() != null)
    {
        children.add(new SelectItem( listType.getId() , listType.getDescription()));
    }
}

我倾向于重构代码以使用单个变量:

for (ListType listType: this.listTypeManager.getSelectableListTypes())
{
    String description = listType.getDescription();

    if (description != null)
    {
        children.add(new SelectItem(listType.getId() ,description));
    }
}

我的理解是 JVM 以某种方式针对原始代码进行了优化,尤其是像 children.add(new SelectItem(listType.getId(), listType.getDescription())); 这样的嵌套调用。

比较这两个选项,哪一个是首选方法,为什么?那是在内存占用、性能、可读性/易用性以及我现在没有想到的其他方面。

后一种代码 sn-p 何时比前者更有利,也就是说,当使用临时局部变量变得更可取时,是否有任何(近似)数量的 listType.getDescription() 调用,因为 listType.getDescription() 总是需要一些存储this对象的堆栈操作?

【问题讨论】:

  • 如果调用次数足够多,JIT 会尽可能对其进行记忆。其余的看我的回答。

标签: java performance readability memory-consumption


【解决方案1】:

我几乎总是更喜欢局部变量解决方案。

内存占用

单个局部变量需要 4 或 8 个字节。这是一个引用,没有递归,所以我们忽略它。

性能

如果这是一个简单的 getter,JVM 可以自己记忆它,所以没有区别。如果这是一个无法优化的昂贵调用,手动记忆会使其更快。

可读性

遵循DRY 原则。在您的情况下,这几乎无关紧要,因为局部变量名称与方法调用一样长,但对于更复杂的情况,它具有可读性,因为您不必在两个表达式之间使用find the 10 differences。如果您知道它们是相同的,那么请使用局部变量来明确。

正确性

假设您的SelectItem 不接受nulls,并且您的程序是多线程的。 listType.getDescription() 的值在此期间可能会发生变化,而您已经敬酒了。

调试

拥有一个包含有趣值的局部变量是一个优势。


省略局部变量的唯一方法是节省一行。所以我只会在真正无关紧要的情况下这样做:

  • 很短的表达方式
  • 不可能同时进行修改
  • 简单的私有最终获取器

【讨论】:

  • “正确性”部分下关于多线程问题的要点。谢谢!
【解决方案2】:

我认为第二种方式肯定更好,因为它提高了代码的可读性可维护性,这是最重要的。除非您编写一个每毫秒都很重要的应用程序,否则这种微优化不会真正帮助您。

【讨论】:

【解决方案3】:

我不确定是否是首选。我更喜欢清晰可读的代码而不是性能代码,尤其是在性能提升可以忽略不计的情况下。在这种情况下,我怀疑几乎没有明显差异(尤其是考虑到 JVM 的优化和代码重写能力)

【讨论】:

    【解决方案4】:

    在命令式语言的上下文中,函数调用返回的值不能被记忆(参见http://en.m.wikipedia.org/wiki/Memoization),因为不能保证函数没有副作用。因此,您的策略确实避免了函数调用,代价是分配一个临时变量来存储对函数调用返回值的引用。 除了稍微更高效(除非函数在循环中多次调用,否则这并不重要),由于更好的代码可读性,我会选择你的风格。

    【讨论】:

      【解决方案5】:

      我同意一切。关于可读性我想补充一点: 我看到很多程序员在做这样的事情:

      如果 (item.getFirst().getSecond().getThird().getForth() == 1 ||

      item.getFirst().getSecond().getThird().getForth() == 2 ||

      item.getFirst().getSecond().getThird().getForth() == 3)

      甚至更糟:

      item.getFirst().getSecond().getThird().setForth(item2.getFirst().getSecond().getThird().getForth())

      如果您多次调用同一个 10 个 getter 链,请使用中间变量。它更容易阅读和调试

      【讨论】:

        【解决方案6】:

        我同意局部变量的可读性方法仅当局部变量的名称是自记录的。称其为“描述”是不够的(哪个描述?)。称它为“selectableListTypeDescription”会更清楚。我会指出,for 循环中递增的变量应该命名为“selectableListType”(特别是如果“listTypeManager”具有其他 ListTypes 的访问器)。

        另一个原因是如果不能保证这是单线程的,或者您的列表是不可变的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-05-26
          • 1970-01-01
          • 2012-10-12
          • 1970-01-01
          • 2019-03-05
          • 1970-01-01
          • 1970-01-01
          • 2013-10-13
          相关资源
          最近更新 更多