public class MyClass {
public static void main(String args[]) {
int minLeadingZeroes = 32;
for (int i = 0; i < 1_000_000; i++) {
int hash = System.identityHashCode(new Object());
minLeadingZeroes = Math.min(minLeadingZeroes, Integer.numberOfLeadingZeros(hash));
}
System.out.println("Smallest number of leading zeroes in identity hash codes of 1000000 objects = " + minLeadingZeroes);
}
}
当使用64位JVM运行时,将打印
Smallest number of leading zeroes in identity hash codes of 1000000 objects = 1
而在32位JVM上,它会打印
Smallest number of leading zeroes in identity hash codes of 1000000 objects = 7
1条答案
按热度按时间cigdeys31#
首先,也是最重要的一点:所有这些都是一个实现细节,没有在规范中定义。我专门讨论了最近的OpenJDK构建(我正在测试JDK 17,但这种行为似乎存在了一段时间),但没有任何迹象表明其他JDK甚至OpenJDK的未来版本可以改变这一点。
接下来,区分对象的 * 身份哈希代码 * 和它的 * 哈希代码 * 很重要。
hashCode()
对此没有影响)。这个值可以通过调用System.identityHashCode(obj)
得到。hashCode()
的返回值。虽然这是一个重要的值,但只要有任何东西存储在HashMap
或HashSet
(或类似的结构)中,JVM本身并不特别关心它。即使它这样做了,它也不能将其存储在对象头中,因为hashCode()
可以想象每次调用它时都会返回不同的值。这两个定义以一种重要的方式相互作用:
java.lang.Object
的hashCode()
方法(以及该方法未被覆盖的任何其他对象)将返回标识哈希代码。因此,如果没有定义其他值,可以说身份哈希代码是哈希代码的默认值。在看了the relevant code之后,在32位平台上确实看起来最多有25位的空间来存储身份哈希代码。
但是
hashCode
被定义为32位宽,那么这是怎么回事呢?很简单:这些平台上的 * 身份哈希码 * 根本不会 * 使用 * 超过25位的任何比特,因此所有未存储的比特都是已知的/假定为零。
虽然我没有找到决定的具体位置(我也没有仔细看),但可以很容易地用这样的代码验证这一点:
当使用64位JVM运行时,将打印
而在32位JVM上,它会打印
当然,这不是绝对的证据,但在测试一百万个对象时,这些值是巧合的可能性极小。
还要注意的是,即使在64位OpenJDK构建中,身份散列代码也最多使用31位(如上面链接的实现的注解中所述),尽管有大量的空闲空间(在这种情况下,许多位未使用)。