通常,默认实现Object.hashCode()
是内存中对象分配地址的某些功能(尽管JLS并未强制执行此功能)。既然VM会在内存中绕开对象,为什么System.identityHashCode()
在对象的生存期内,返回的值从不改变?
如果这是一次“一次性”计算(对象的hashCode
计算一次,并存放在对象标题或其他内容中),那么这是否意味着两个对象可能具有相同的对象identityHashCode
(如果它们恰好是第一次分配给对象)内存中的相同地址)?
通常,默认实现Object.hashCode()
是内存中对象分配地址的某些功能(尽管JLS并未强制执行此功能)。既然VM会在内存中绕开对象,为什么System.identityHashCode()
在对象的生存期内,返回的值从不改变?
如果这是一次“一次性”计算(对象的hashCode
计算一次,并存放在对象标题或其他内容中),那么这是否意味着两个对象可能具有相同的对象identityHashCode
(如果它们恰好是第一次分配给对象)内存中的相同地址)?
Answers:
现代JVM将值保存在对象标头中。我认为,通常仅在首次使用时才计算该值,以使花费在对象分配上的时间最小化(有时低至十几个周期)。可以编译通用的Sun JVM,以便所有对象的身份哈希码始终为1。
多个对象可以具有相同的身份哈希码。这就是哈希码的本质。
回答第二个问题,与实现方式无关,多个对象可能具有相同的identityHashCode。
有关Javadoc中的措辞的简短讨论以及用于演示非唯一性的程序,请参见错误6321873。
identityHashCode
绝不会返回已经与其他任何存在的对象一起返回的值。根据内存管理器的实现方式,这可能会很昂贵,或者可能会增加零附加成本。例如,一个Object
可能包含一个指向指针表的索引,每个对象只要存在就不变地分配了一个表槽。典型的JVM实现无法做到这一点……
identityHashcode
线程安全。如果从未检索过对象的哈希码,是否可以保证identityHashCode
在该对象上同时进行“首次”调用会产生相同的值?
HotSpot中对象的标头由类指针和“标记”字组成。
可以在markOop.hpp
文件中找到标记词的数据结构的源代码。在此文件中,有一条注释描述了标记字的内存布局:
hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
在这里,我们可以看到在32位系统上,普通Java对象的身份哈希码保存在标记字中,它的长度为25位。
据我所知,这是为了返回引用而实现的,该引用在对象生命周期中永远不会改变。