传递接口和类重新加载之间的性能差异

人们一致认为使用接口比使用类更好。 我当然同意:接受ArrayList而不是List的库方法将是一个废话。

人们也一致认为,表现总是一致的。 在这里,我的基准测试有所不同。 结果 接口和抽象类都有1到4个实现。 当使用两个以上的实现时,性能开始出现分歧。 我正在寻找这种行为的解释(以及错误共识的起源)。

人们一致认为使用接口比使用类更好。

这太简单了。 接口和抽象类都具有相互优势。

您链接的答案建议将变量声明为java.util.List,而不是尽可能将java.util.ArrayList声明。 使用List为您提供更大的灵活性以便以后选择不同的实现类是正确的,因此当您不需要特定于ArrayList的方法(例如, .trimToCapacity() )时,这是一件好事。 但是,这个建议通常与接口或类无关,如果java.util.List是抽象类,那么它也同样如此。

人们也一致认为,表现总是一致的。

流行的建议是,不应该担心类和接口之间的性能差异,而应该根据良好的编程原则在它们之间进行选择。 这是一个很好的建议,可以防止程序员担心不重要的性能差异; 然而,有时会误解为暗示没有差异,这是不正确的。 有一点不同:课程更快。

通过类调用方法,在类中的固定偏移量处有一个vtable ,并且在该表中的已知偏移处找到指向所需方法实现的指针,因此跳转到目标非常简单。 但是,虽然类只能扩展一个超类,但是类可以实现任意数量的接口,因此通过接口进行方法调用会更复杂。 对于接口调用,它必须首先查找类的接口列表以找到所需的接口,然后才能在该接口的表中查找方法实现。

当使用两个以上的实现时,性能开始出现分歧。

无论是使用类还是接口,多态调用都会导致CPU上的管道刷新,因为CPU无法提前看到跳转的目标,而且成本很高。 当调用站点在运行时被认为是寡形的( 寡核苷酸意为“少数”)时,性能会急剧增加,因为一个好的JVM专门处理这些情况。 对于单形情况,JVM可以直接跳转到单个目标方法,甚至可以内联它。 对于它实现of();的二态情形of(); 好像是(不是有效的语法): if (o.getClass() == A.class) A::f(o) else B::f(o);

实际上我不确定为什么二态情况在你的基准测试中看起来和单态情况一样快 – CPU的分支预测器是否应该在随机数据的一半时间内错误? 也许还有其他微妙的工作……

也可以看看: