Java 什么';s 64位JVM上对象的默认哈希值

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位整数,该

由于对象的默认哈希值是对象的对象地址,因此在32位机器上,考虑哈希值是int值是有意义的。我的问题是,在64位机器上,地址应该是64位的,对吗?那么32位整数散列值呢?是否会有一些下转换(从64位到32位)?

在Java中,
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
    的开头

据我所知和测试,在压缩oops模式下,从JVM内部地址获取本机地址最可靠的方法是使用“sun.JVM.hotspot.memory.Universe”类的方法和方法,如果使用“Java热点服务性代理”

本机地址可以计算为:

if (compressedRef) {
    return (address >> compressRefShift) - compressRefBase;
} 
else {
    return address;
}
这里是它的用法


如何计算标识哈希代码是JVM特有的

对于HotSpot JVM,它使用随机数生成器进行标识哈希代码的初始计算。在第一次计算哈希代码后,它会保存在对象头中,以便后续调用
identityHashCode
将返回相同的值

实际上,生成标识哈希代码的算法可以通过
-XX:hashCode
JVM选项进行更改。请看。有以下选项:

  • -XX:hashCode=0
    -global Park Miller RNG(在Java 7之前为默认值)
  • -XX:hashCode=1
    -函数(对象地址,全局状态)
  • -XX:hashCode=2
    -常量1。所有对象都将具有相同的hashCode。仅用于测试
  • -XX:hashCode=3
    -增量计数器
  • -XX:hashCode=4
    -堆中对象地址的低32位
  • -XX:hashCode=5
    -线程本地Marsaglia的Xor移位RNG(自Java 8以来的默认值)

是的,我理解。我的问题是64位JVM上对象的默认哈希值是多少?它仍然是对象地址值吗?@BillRanderson一种根据Javadoc生成32位
int
的算法。一个对象的默认哈希值是地址,大约在1997年。它从未被指定为地址。它已经被n指定为32位。从技术上讲,Object.hashCode()只有31位,也就是说,它永远不会是负数。它与地址无关,尽管打印时它可能看起来像一个地址,并且Javadoc提到“内部地址”“通过在一行中打印多个对象的object.hashCode,可以快速判断它不是地址。你希望看到一个模式,但你没有。@Peter Lawrey:有没有任何规范可以保证
if (compressedRef) {
    return (address >> compressRefShift) - compressRefBase;
} 
else {
    return address;
}