maa*_*nus 36 java performance hashcode
通常,Java会根据给定调用端遇到的实现数量来优化虚拟调用.当你看一下,这可以很容易地在我的基准测试结果中看到,这是一个返回存储的简单方法.这是微不足道的myCodeint
static abstract class Base {
abstract int myCode();
}
Run Code Online (Sandbox Code Playgroud)
与几个完全相同的实现
static class A extends Base {
@Override int myCode() {
return n;
}
@Override public int hashCode() {
return n;
}
private final int n = nextInt();
}
Run Code Online (Sandbox Code Playgroud)
随着实现数量的增加,方法调用的时序从两个实现的0.4 ns增加到1.2 ns,再增长到11.6 ns,然后缓慢增长.当JVM看到多个实现时,preload=true即时序略有不同(因为instanceof需要进行测试).
到目前为止,一切都很清楚,但hashCode行为却相当不同.特别是,在三种情况下,它慢了8-10倍.知道为什么吗?
我很好奇,如果hashCode可以通过手动调度来帮助穷人,那可能会很多.

几个分支完美地完成了这项工作:
if (o instanceof A) {
result += ((A) o).hashCode();
} else if (o instanceof B) {
result += ((B) o).hashCode();
} else if (o instanceof C) {
result += ((C) o).hashCode();
} else if (o instanceof D) {
result += ((D) o).hashCode();
} else { // Actually impossible, but let's play it safe.
result += o.hashCode();
}
Run Code Online (Sandbox Code Playgroud)
请注意,编译器避免了对两个以上实现的这种优化,因为大多数方法调用比简单的字段加载要昂贵得多,并且与代码膨胀相比,增益会很小.
最初的问题" 为什么JIT不优化hashCode其他方法 "仍然存在并hashCode2证明它确实可以.
看起来好极了,至少在这个说明中
调用任何扩展Base的类的hashCode()与调用Object.hashCode()相同,这就是它在字节码中编译的方式,如果你在Base中添加一个显式hashCode来限制潜在的调用目标调用Base.hashCode() .
我不完全确定发生了什么,但宣布再次Base.hashCode()具有hashCode竞争力.

好的,提供帮助的具体实现Base#hashCode,但是,JIT必须知道它永远不会被调用,因为所有子类都定义了它们(除非另外一个子类被加载,这可能导致去优化,但这对于JIT来说并不新鲜) .
所以它看起来像错过了优化机会#1.
提供相同的抽象实现Base#hashCode.这是有道理的,因为它提供了确保不需要进一步查找,因为每个子类必须提供它自己的(它们不能简单地从它们的祖父母继承).
仍然有两个以上的实现,myCode速度要快得多,编译器必须做一些不理想的事情.也许错过了优化机会#2?
Eri*_*las -2
hashCode() 的语义比常规方法更复杂,因此当您调用 hashCode() 时,JVM 和 JIT 编译器必须比调用常规虚拟方法时执行更多的工作。
一种特殊性会对性能产生负面影响:在空对象上调用 hashCode() 是有效的并返回零。这需要比常规调用多一个分支,这本身就可以解释您所说的性能差异。
请注意,由于引入了具有此语义的 Object.hashCode(target),这似乎只是从 Java 7 开始的。了解您在哪个版本上测试了此问题以及您在 Java6 上是否有相同的情况将会很有趣。
另一个特性对性能有积极的影响:如果您不提供自己的 hasCode() 实现,JIT 编译器将使用内联哈希码计算代码,该代码比常规编译的 Object.hashCode 调用更快。
E.