【问题标题】:why is object location swapped in JVM? [duplicate]为什么在JVM中交换对象位置? [复制]
【发布时间】:2013-12-30 16:03:28
【问题描述】:

这是我尝试过的:

public final class firstObj{    
    public static void main(String args[]){    
        Object obj = new Object();    
        Object obj1 = new Object();    
        System.out.println(obj);
        System.out.println(obj1);    
    }    
}

我一开始编译了程序,连续运行了两次, 我得到了两个不同的输出:

输出1:

java.lang.Object@6f548414
java.lang.Object@65ab7626

输出 2:

java.lang.Object@659c2931
java.lang.Object@6f548414

想知道为什么JVM在第二次运行的时候把第二个对象的位置换成第一个对象的位置了..,很迷惑..,

【问题讨论】:

  • 你为什么在乎?我不是要刻薄。原因深藏在特定于平台的 java 内存管理的细节中。除非您正在做一些异国情调/高级的事情,否则请相信 java 会解决这个问题。如果您有某些特定需求,那么您可能会采取有问题的方法。
  • 该数字基于对象的堆地址。真的很随意。
  • 两个对象,即使它们在您看来相同,也不一定在内存中的位置相同。字符串是唯一的,因为两个字符串可能共享相同的地址,因为它们是相同的(这里发生了 JVM 魔术)。
  • @SnakeDoc - 如果两个字符串“共享”同一个地址,它们就是同一个对象。
  • @HotLicks 这就是我的意思......也许我可以更清楚。

标签: java


【解决方案1】:

hashCode() 与内存中的位置关系不大。虽然它可能看起来像一个地址,但它只是一个随机生成的数字。

我没想到它总是从同一个段分配,但我想知道对象是否是按顺序分配的,因为我的输出显示了一点这样的模式

意料之中,因为它是一个随机数。


如果你在 OpenJDK 或 Oracle 热点上运行它,你会得到。

import sun.misc.Unsafe;

import java.lang.reflect.Field;

public class ObjectAddress {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();
        Object o3 = new Object();
        Object[] os = {o1, o2, o3};
        System.out.println("Before using hashCode");
        for (int i = 0; i < os.length; i++) {
            int address = UNSAFE.getInt(os, UNSAFE.arrayBaseOffset(Object[].class) + i * 4);
            int hashCode = UNSAFE.getInt(os[i], 1L);
            System.out.println(i + ": " + Integer.toHexString(address) + " hashCode " + Integer.toHexString(hashCode));
            os[i].hashCode();
        }
        System.out.println("After using hashCode");
        for (int i = 0; i < os.length; i++) {
            int address = UNSAFE.getInt(os, UNSAFE.arrayBaseOffset(Object[].class) + i * 4);
            int hashCode = UNSAFE.getInt(os[i], 1L);
            System.out.println(i + ": " + Integer.toHexString(address) + " hashCode " + Integer.toHexString(hashCode) + " for " + os[i]);
            UNSAFE.putInt(os[i], 1L, 0x12345678);
        }
        System.out.println("After setting the hashCode");
        for (int i = 0; i < os.length; i++) {
            int address = UNSAFE.getInt(os, UNSAFE.arrayBaseOffset(Object[].class) + i * 4);
            int hashCode = UNSAFE.getInt(os[i], 1L);
            System.out.println(i + ": " + Integer.toHexString(address) + " hashCode " + Integer.toHexString(hashCode) + " for " + os[i]);
            os[i].hashCode();
        }

    }

    static final Unsafe UNSAFE;

    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            UNSAFE = (Unsafe) theUnsafe.get(null);
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}

你会得到类似的东西

Before using hashCode
0: d8e78160 hashCode 0
1: d8e78170 hashCode 0
2: d8e78180 hashCode 0
After using hashCode
0: d8e78160 hashCode 68111f9b for java.lang.Object@68111f9b
1: d8e78170 hashCode 3c322e7d for java.lang.Object@3c322e7d
2: d8e78180 hashCode 3e2f1b1a for java.lang.Object@3e2f1b1a
After setting the hashCode
0: d8e78160 hashCode 12345678 for java.lang.Object@12345678
1: d8e78170 hashCode 12345678 for java.lang.Object@12345678
2: d8e78180 hashCode 12345678 for java.lang.Object@12345678

可以看到每个 Object 相隔 16 个字节。

【讨论】:

  • 内存中的位置可能会影响哈希码值“这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 编程语言不需要这种实现技术。 "来自hashcode()
  • 我喜欢 sun 有一个名为Unsafe的包
  • @vandale 可以,但它似乎只是随机种子的一部分,但 AFAIK 通常不是。
  • source code 有点暗示正常的事情是使用地址。
  • (但是无论如何,哈希码应该被认为是“随机的”,与创建对象的时间/地点没有关系——任何明显的关系至少不是“架构的”(如果不是完全巧合)。)
【解决方案2】:

与 C 不同,在 C 语言中,对新对象的请求会返回一个指针,该指针将在其整个生命周期中用相同的唯一位模式表示,大多数 Java 实现使用可以随着程序运行而更改的位模式存储引用(对象引用可能将对象的物理位置存储在内存中;如果 GC 想要移动对象,它将暂停所有线程,更新存在于 Universe 中任何位置的对该对象的每个引用以指向新位置,然后让线程继续)。拥有一个不可更改的、大部分唯一的 32 位数字来描述每个对象几乎与拥有一个不可更改的 32 位值一样有用曾经创建过,但更便宜且更易于实现(保持 64 位值将使每个对象所需的存储空间增加 8 个字节,但具有在所有对象之间建立永久排名的优势,这在某些情况下很有用例如多锁获取或缓存;虽然为对象建立永久排名有时很有用,但让需要这种排名的类自己实现它可能比让系统执行它更好[尽管有一个系统方法可能会有所帮助如果从多个线程调用将返回一个保证唯一的 64 位数字,该数字将在每个线程上严格增加,并且总体上与对象年龄大致对应,但是h 将避免AtomicLong 增量的成本]

对象的默认字符串表示中包含的十六进制数字没有任何意义,只是任何给定对象总是报告相同的数字,而不同的对象通常报告不同的数字。我不知道是否有任何特殊原因使数字看起来是随机的,而不是从零开始计数然后换行,但我猜 Java 的实现者希望阻止人们做任何依赖对象哈希的事情值之间有任何关系。

【讨论】:

    猜你喜欢
    • 2014-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    • 2021-07-13
    • 2017-12-31
    • 2019-05-10
    • 2013-01-03
    相关资源
    最近更新 更多