【问题标题】:In Java is there a performance difference between referencing a field through getter versus through a variable?在 Java 中,通过 getter 引用字段与通过变量引用字段之间是否存在性能差异?
【发布时间】:2009-07-28 18:25:41
【问题描述】:

做有什么区别

Field field = something.getSomethingElse().getField();
if (field == 0) {
//do something    
}
somelist.add(field);

if (something.getSomethingElse().getField() == 0) {
//do something    
}
somelist.add(something.getSomethingElse().getField());

通过 getter 对字段的引用会导致性能损失还是与引用分配的变量一样? 我知道该变量只是对内存空间的引用,所以 getter 应该只是获取该内存空间的另一种方式。

请注意,这是一个学术问题(只是好奇的学校),而不是一个实际问题。

【问题讨论】:

  • 请注意,如果您在多线程环境中工作,两者之间也可能存在差异,因为在 //do something 部分期间,其他线程可能会更改 something.getSomethingElse()。导致 somelist 的附加值在两个代码部分之间有所不同。

标签: java performance variables reference getter


【解决方案1】:

这是一个可以忽略不计的损害。不要太在意它,否则你会成为过早优化的牺牲品。如果您的应用程序很慢,这不是原因。

【讨论】:

    【解决方案2】:

    假设getSomethingElse()被定义为

    public SomethingElse getSomethingElse() {
        return this.somethingElse;
    }
    

    性能差异将是最小的(如果它会被内联,则为零)。然而,在现实生活中,你不能总是确定是这样 - 可能在幕后发生了一些处理(不一定在对象本身中,而是通过 AOP 代理)。因此,将结果保存在变量中以供重复访问可能是个好主意。

    【讨论】:

      【解决方案3】:

      不同之处在于,通过 getter 访问变量会导致方法调用。可以想象,JVM 在某些情况下可以优化方法调用,但它方法调用。

      也就是说,如果您的代码中最大的瓶颈或性能问题是访问器方法的开销,我想说您不必担心太多。

      【讨论】:

        【解决方案4】:

        存在性能损失(可能很小,可以忽略不计)但是,JVM 可能会内联这个和所有调用以提高性能。

        如果你以第二种方式离开它会更好。

        【讨论】:

        • 更不用说第二种方式可读性更强,代码更少。
        【解决方案5】:

        如果你有一个好的 JVM,比如 Sun 的 HotSpot,就不会。它将内联并编译(本地代码)getter。

        使用 getter 通常是一种非常好的做法,作为一种防御措施和一般信息隐藏。

        【讨论】:

          【解决方案6】:

          如果使用 Java 编写 Android 应用程序需要注意的一点是: http://developer.android.com/training/articles/perf-tips.html#GettersSetters

          在 C++ 等本地语言中,通常使用 getter(i = getCount()) 而不是直接访问该字段 (i = mCount)。这 是 C++ 的一个很好的习惯,并且经常在其他对象中练习 像 C# 和 Java 这样的面向语言,因为编译器通常可以 内联访问,如果您需要限制或调试字段访问 您可以随时添加代码。

          但是,这在 Android 上是个坏主意。 虚拟方法调用是 昂贵,比实例字段查找贵得多。这是合理的 遵循常见的面向对象编程实践并具有 公共接口中的 getter 和 setter,但在一个类中 应始终直接访问字段。

          在没有 JIT 的情况下,直接字段访问比调用 微不足道的吸气剂。使用 JIT(直接字段访问与 访问本地),直接字段访问比直接访问快 7 倍 调用一个普通的 getter。

          请注意,如果您使用 ProGuard,则可以兼得两者的优点 世界,因为 ProGuard 可以为您内联访问器。

          【讨论】:

            【解决方案7】:

            如果该方法是一个简单的 getter,不涉及任何处理,这不是问题。如果它涉及大量计算,那么一个属性无论如何也不会做你想做的。

            唯一一次我会担心任何差异是在具有大量迭代(数千次)的紧密循环中。即使这样,如果您使用方面来编织额外的处理(例如日志记录),这可能只是一个问题,这可能涉及创建数千个额外的对象(例如 JoinPoints 和参数自动装箱)和由此产生的 GC 问题。

            【讨论】:

              【解决方案8】:

              我不会担心性能差异。您最好不要考虑它,而是花时间在现实场景中分析您的代码。您很可能会发现程序的慢速部分并非您认为的那样。

              【讨论】:

                【解决方案9】:

                这篇文章讨论的是 CLI VM 而不是 JVM,但每个都能够做类似的事情,所以我相信它是相关的。

                我正在以一种特殊的方式为我的 JIT 处理这个特殊问题。请注意,这里的描述是概念性的,出于性能原因,代码以稍微不同的方式实现它。当我加载一个程序集时,如果它只是返回一个成员字段,我会在方法描述符中做一个注释。当我稍后 JIT 其他方法时,我将字节码中的所有 call 指令替换为 ldfld 指令,然后将其传递给本机代码生成器。这样,我可以:

                1. 在 JIT 中节省时间(ldfld 在 JIT 中花费的处理器时间比 call 少)。
                2. 即使在 baseline compiler 中也是内联属性。
                3. 总的来说,在分离调试器时,使用公共属性/私有字段模式不会导致任何类型的性能损失。 (当附加了调试器时,我无法内联访问器。)

                我毫不怀疑,VM 技术领域的知名人士已经在他们的产品中实现了与此类似(并且可能更好)的东西。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2013-09-30
                  • 2011-03-11
                  • 1970-01-01
                  • 2020-03-27
                  • 2012-06-22
                  • 2019-07-05
                  • 2010-09-24
                  • 1970-01-01
                  相关资源
                  最近更新 更多