Java 分析:java.lang.Object.hashCode 占用一半的 CPU 时间但从未显式调用

Posted

技术标签:

【中文标题】Java 分析:java.lang.Object.hashCode 占用一半的 CPU 时间但从未显式调用【英文标题】:Java profiling: java.lang.Object.hashCode takes half of the CPU time but never explictly called 【发布时间】:2010-06-26 16:51:13 【问题描述】:

我使用-agentlib:hprof=cpu=samples 对我的多线程程序进行了基准测试 并惊讶地在结果中发现以下行:

rank   self  accum   count trace method
   1 52.88% 52.88%    8486 300050 java.lang.Object.hashCode

我从未在我的程序中明确调用 hashCode()。 这可能是什么原因?如何理解这次“浪费”的来源,是否正常?

谢谢, 大卫

【问题讨论】:

如果您首先解释一下为什么这是一个问题,那就太好了。 【参考方案1】:

您很可能正在大量使用 Map,例如 HashMap。

HashMap 使用hashCode 来分发对象。如果您使用这种数据结构的许多对象,那么您的.equals.hashCode 方法的正确实现非常重要。

参见:有效的 Java 项目 8:Always override hashCode when you override equals

【讨论】:

谢谢!你可能是对的。我实际上可以放弃对随机访问功能的使用(你是这么称呼它的吗?),而且我不在乎对象的顺序。我只需要能够添加对象然后遍历所有对象。此外,这确实是一个集合(我不需要多次使用同一个对象),但我也永远不会尝试多次添加它......我应该使用列表来代替(虽然我不在乎订购)?对于这样的集合,最有效的数据结构是什么? 确实是一个HashSet。我现在将所有内容都转换为 ArrayLists。现在这条线从分析器中消失了,但总运行时间仍然非常相似。奇怪的。不是吗?! @David B 可能,也可能不是。您可能对不同的数据结构进行基本相同的操作。您可能会考虑不同的算法,但从这里很难说。 :)【参考方案2】:

你应该做的一件事是检查匹配的堆栈跟踪,看看是谁在调用它;变化是它确实是HashMap。

但除此之外,我注意到 hprof 往往会大大高估对 hashCode(); 的调用。我真的很想知道如何以及为什么。这是基于实际了解代码的粗略性能概况;我已经看到 50% 的 CPU 使用率(通过采样),几乎可以肯定它绝对不会花费那么长时间。 hashCode() 的实现只返回一个 int 字段,方法是最终的(在最终对象上)。 所以它基本上是某种分析器工件......只是不知道如何或为什么,或者如何摆脱它。

【讨论】:

【参考方案3】:

你可能是对的。我实际上可以放弃对随机访问功能的使用(你是这么称呼它的吗?),而且我不在乎对象的顺序。我只需要能够添加对象然后遍历所有对象。此外,这确实是一个集合(我不需要多次使用同一个对象),但我也永远不会尝试多次添加它......我应该使用列表来代替(虽然我不在乎订购)?这种集合最有效的数据结构是什么?

HashSet 被实现为将键映射到自身的 HashMap,因此切换到 HashSet 不会产生太大的性能差异。

其他替代方法是 TreeSet,或者(假设您的应用程序永远不会尝试插入重复项)List 类之一。如果您的应用程序可以使用 List,那么 ArrayList 或 LinkedList 将比 HashSet 或 TreeSet 更有效。

但是,您的应用程序将 50% 的时间花在 hashCode 方法上,这很可疑。除非调整哈希表的大小,否则每个 set 或 map 操作只应调用一次 hashCode 方法。所以要么有很多地图/集合调整大小,要么你正在做大量的集合add操作。 (AFAIK,Object hashcode 方法很便宜,所以每次调用的成本应该不是问题。)

编辑

nextInt() 真的很贵吗?有其他选择吗?

不,它不贵。看一下代码。 Random 类(和 nextInt() 方法)确实使用了 AtomicLong 来使其成为线程安全的,如果您编写了非线程安全的版本,您可能会节省几个周期。源码在你的JDK安装目录下……看看吧。

【讨论】:

确实有很多添加操作。如前所述,我切换到 ArrayList 但总时间略有改善。现在,排名靠前的 CPU 时间消耗者是: rank self accum count trace method 1 19.30% 19.30% 2727 300122 java.util.Random.next 我确实多次调用 rand.nextInt() 来获取 0 到 ~ 10^7 之间的整数. nextInt() 真的很贵吗?有其他选择吗?

以上是关于Java 分析:java.lang.Object.hashCode 占用一半的 CPU 时间但从未显式调用的主要内容,如果未能解决你的问题,请参考以下文章

java.lang.Object底层代码分析-jdk1.8

为啥 java.lang.Cloneable 不覆盖 java.lang.Object 中的 clone() 方法?

java.lang.Class java.lang.Object.getClass()' 在空对象引用上

maven找不到java.lang.object的类文件

ClassCastException:java.lang.Object[] 无法转换为 java.lang.String[] android

Spring mvc的jdbc查询:queryForInt(java.lang.String sql, java.lang.Object... args)