Java - 接口/基类引用 - 性能

10

在声明对象时,使用接口/基类引用是一种通用的编程实践,例如:

InterfaceIF ref = new SomeObject();

我了解这提供了松耦合,我们可以改变/编写新的类和新的实现而不会对大量代码产生影响。

这里也很好地解释了这一点。

但有一件事情我正在尝试理解,且在该问题中未得到回答:

  • 使用接口/基类引用会影响性能吗?
  • 如果会,这会带来积极的还是消极的影响?

几乎可以确定99.999%的情况下,使用接口不会影响性能。我不知道它会如何影响性能。你为什么认为它会影响性能呢? - mwhs
@mwhs:实际上是这样的。虚拟调用可能会非常昂贵,但JVM消除了(几乎?)所有这些开销。 - maaartinus
但如果JVM消除了开销,那么最终成本不是一样的吗? - mwhs
1
无论如何,如果存在性能损失(我怀疑这一点,因为我从未见过有令人信服的证据),那么它是微不足道的,并且当使用接口编程时,它绝对不会抵消程序在可移植性和可扩展性方面获得的巨大好处。此外,它使您的代码可测试(依赖注入允许模拟或存根依赖项),这对我来说应该足够理由去这样做。 - Guillaume
@Guillaume:我真的没有说你不应该这样做。但是你不应该怀疑真相。在Java的黑暗时代,程序员们出于这个原因被建议将他们的类设置为final。使invokevirtual像invokedynamic或invokestatic一样快是一个非常重要的成就。 - maaartinus
1个回答

9

直接使用类可能更快,但不会更慢。如果JVM看到一个具体的类,它就知道“该调用谁”。除非这个类是 final 的,否则可能并不完全准确,因为可能会有子类存在,甚至是之后才被JVM加载的。

  • 对于一个 final 类,方法调用可以被优化为本地 CALL 指令。这是最简单的情况。

  • 如果类不是 final 的,但还没有加载子类,那么只需要在开始处加上一个额外的检查即可。当检查失败时,JVM 必须丢弃这个过度优化的编译方法并重新编译(没什么大不了的)。

  • 当存在子类时,一切都取决于在给定调用点上实际遇到了多少个子类。如果只有一个,那么快速检查就足以验证给定的类是否是预期的类(通过将此测试移出循环等方式,这个开销变得可以忽略不计)。

  • 显然,具有更多候选者的情况会更慢(可以搜索 bimorphic 和 megamorphic)。

显然,没有任何东西能使通过接口调用变快。

如果有多个实现,并且从一个调用点调用了多个实现,则存在虚拟调用分派的开销。更多详细信息请参见此答案此基准测试


6
“直接使用类可能更快,但永远不会更慢。” 这句话的意思是,如果你直接使用一个类,它的执行速度可能会比其他操作更快,而不是更慢。 - mwhs
1
@mwhs:解释得够了吗?这就像从你的电话簿中打电话给某人,而不是先打电话给信息服务。 - maaartinus
1
@maaartinus 好的。但如果您能发布证明的链接,那就太棒了。=) - Sergey Morozov
@frostjogla:随意编写基准测试。忽略所有预热和其他好的建议,你就可以看到差异了。当JIT工作得很好时,你会看到相同的时间吧,我猜。 - maaartinus
@maaartinus 谢谢。我被“INVOKEVIRTUAL vs INVOKEINTERFACE”搜索到了:http://www.kaffe.org/pipermail/kaffe/1998-September/172057.html 和 https://www.usenix.org/legacy/events/coots01/dutchyn/dutchyn_html/index.html - Sergey Morozov
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接