Java 什么';s 64位JVM上对象的默认哈希值
由于对象的默认哈希值是对象的对象地址,因此在32位机器上,考虑哈希值是int值是有意义的。我的问题是,在64位机器上,地址应该是64位的,对吗?那么32位整数散列值呢?是否会有一些下转换(从64位到32位)?在Java中,Java 什么';s 64位JVM上对象的默认哈希值,java,hash,Java,Hash,由于对象的默认哈希值是对象的对象地址,因此在32位机器上,考虑哈希值是int值是有意义的。我的问题是,在64位机器上,地址应该是64位的,对吗?那么32位整数散列值呢?是否会有一些下转换(从64位到32位)?在Java中,int是32位。即使是在64位本机上。请参见Integer.SIZE。此外,甲骨文的教程(部分)说- int:默认情况下,int数据类型是一个32位有符号二补整数,最小值为-231,最大值为231-1。在JavaSE8及更高版本中,可以使用int数据类型表示无符号32位整数,该
int
是32位。即使是在64位本机上。请参见Integer.SIZE
。此外,甲骨文的教程(部分)说-
int:默认情况下,int数据类型是一个32位有符号二补整数,最小值为-231,最大值为231-1。在JavaSE8及更高版本中,可以使用int数据类型表示无符号32位整数,该整数的最小值为0,最大值为232-1。使用Integer类将int数据类型用作无符号整数。有关更多信息,请参阅数字类一节。向Integer类添加了静态方法,如compareUnsigned、divideUnsigned等,以支持无符号整数的算术运算
Javadoc说
(这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要这种实现技术。)
我的问题是64位JVM上对象的默认哈希值是多少?它仍然是对象地址值吗
“默认”值。。。或者更具体地说,没有指定如何计算对象的“标识哈希代码”。不是在32位JVM或64位JVM上
可以观察到,该值通常基于首次调用System.identityHashcode()
方法时对象的地址,但这只是一个观察。它当然没有被指定为那样,这意味着不同的JVM可以自由地以不同的方式实现它
当然,它不能是64位JVM上的实际地址。。。因为64位地址不适合32位整数。很明显
无论实际计算结果如何,标识哈希代码始终是对象地址的可靠代理。如果一个具有标识hashcode的对象在垃圾收集周期中幸存下来,那么GC可能已经移动了它,并且它的hashcode和地址从此不再相关。(有一点是,可以保证对象的标识哈希代码不会更改。如果更改了,哈希表将中断。)在OpenJDK/hospot JVM中,您可以获得引用的原始索引地址。对于小堆,转换为32位地址;对于大堆,转换为64位地址。对于中等大小的堆,使用32位 该实现在AFAIK中可用,最后一个实现是实际使用的实现
/**
* Prints the addresses and hashCode()s of the first objects in eden space.
* <p>
* Assumes a 32-bit address space e.g. CompressOops
* </p>
*/
public static void main(String... ignored) throws Exception {
Object[] objects = new Object[8];
for (int i = 0; i < 20; i++) {
System.gc();
for (int j = 0; j < objects.length; j++) {
objects[j] = new Object();
}
for (int j = 0; j < objects.length; j++) {
long address = OBJECT_SCALE == 4 ? UNSAFE.getInt(objects, OBJECT_BASE + j * OBJECT_SCALE) & 0xFFFFFFFFL : UNSAFE.getLong(objects, OBJECT_BASE + j * OBJECT_SCALE);
System.out.printf("%,d: addr: %8x, hc: %8x ", j, address, objects[j].hashCode());
}
System.out.println();
}
}
static final Unsafe UNSAFE;
static final int OBJECT_BASE, OBJECT_SCALE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
OBJECT_BASE = UNSAFE.arrayBaseOffset(Object[].class);
OBJECT_SCALE = UNSAFE.arrayIndexScale(Object[].class);
} catch (Exception e) {
throw new AssertionError(e);
}
}
对于3-30 GB之间的压缩doop和heap,它可以让您了解内存位置(但它是经过翻译的)。在上述情况下,地址是index*8
,8
来自默认为8字节的分配块大小。在Java8中,它可以增加到16或32,尽管使用16只对32-64GB的堆有用,而32可能根本没有用处,因为32字节的分配大小可能比使用64位引用更昂贵
如您所见,每个对象都是在System.gc()后面的相同空间中分配的。地址每次都是相同的,但是哈希代码
- 与地址无关
- 只有31位,在十六进制中没有从
到8
的开头F
if (compressedRef) {
return (address >> compressRefShift) - compressRefBase;
}
else {
return address;
}
这里是它的用法
如何计算标识哈希代码是JVM特有的 对于HotSpot JVM,它使用随机数生成器进行标识哈希代码的初始计算。在第一次计算哈希代码后,它会保存在对象头中,以便后续调用
identityHashCode
将返回相同的值
实际上,生成标识哈希代码的算法可以通过-XX:hashCode
JVM选项进行更改。请看。有以下选项:
-global Park Miller RNG(在Java 7之前为默认值)-XX:hashCode=0
-函数(对象地址,全局状态)-XX:hashCode=1
-常量1。所有对象都将具有相同的hashCode。仅用于测试-XX:hashCode=2
-增量计数器-XX:hashCode=3
-堆中对象地址的低32位-XX:hashCode=4
-线程本地Marsaglia的Xor移位RNG(自Java 8以来的默认值)-XX:hashCode=5
int
的算法。一个对象的默认哈希值是地址,大约在1997年。它从未被指定为地址。它已经被n指定为32位。从技术上讲,Object.hashCode()只有31位,也就是说,它永远不会是负数。它与地址无关,尽管打印时它可能看起来像一个地址,并且Javadoc提到“内部地址”“通过在一行中打印多个对象的object.hashCode,可以快速判断它不是地址。你希望看到一个模式,但你没有。@Peter Lawrey:有没有任何规范可以保证
if (compressedRef) {
return (address >> compressRefShift) - compressRefBase;
}
else {
return address;
}