【问题标题】:How does Object.toString() get the "memory address" and how can I mimic itObject.toString() 如何获得“内存地址”以及如何模仿它
【发布时间】:2016-04-02 17:10:54
【问题描述】:

ObjecttoString 方法的独特之处在于它似乎是 Java 中唯一可以查看内存地址的地方。 Object 是怎么做到的?

我想知道这样我就可以在我自己的类中模仿它的实现。我不能使用super.toString(),因为我正在扩展一个已经覆盖toString 的类。

更新:我的问题的前提是要求内存地址,但是答案已经表明这个前提是不正确的,所以我真正要问的是:Object.toString()如何返回它的作用,以及如何我可以模仿吗?

【问题讨论】:

  • 你有没有试过检查对象类的源代码
  • 强制性问题:你为什么要这样做?
  • 查看 API 文档。这不是内存地址。还有一种方法可以在不使用toString() 的情况下获得相同的效果。
  • @mmirwaldt,没有。 Object.toString()使用的是实际的哈希码实现,不是System.identityHashCode,而且System.identityHashCode与内存地址无关。

标签: java memory tostring


【解决方案1】:

不是内存地址,是hashCode()。另请参阅Object.toString(),其中表示(部分)

Object 类的 toString 方法返回一个字符串,该字符串由对象作为实例的类的名称、at 符号字符 @ 和哈希码的无符号十六进制表示形式组成。物体。换句话说,这个方法返回一个字符串等于:

getClass().getName() + '@' + Integer.toHexString(hashCode())

Object.hashCode()(这通常通过将对象的内部地址转换为整数来实现,但 Java™ 编程语言不需要这种实现技术。) 所以它不是要求是内存地址,如果使用,它仅对Object 的内部(本机)实现可见。

【讨论】:

  • 这个答案是最有帮助的答案,所以我选择它,即使其他答案也很有帮助(例如我可以使用System.identityHashCode
  • Java 很糟糕,这是另一个例子。 '@' 符号读作 'at',因此如果您看到 object@f024,您会直观地认为该对象位于 at 该内存地址。使用“@”符号是一个糟糕的设计决定。
  • @Jabba 您建议的替代默认 Object.toString() 会使用什么?您会在哪里记录?
【解决方案2】:

Object 的 toString 方法的独特之处在于它似乎是 Java 中唯一可以查看内存地址的地方。 Object 是如何做到这一点的?

它没有获取地址,在 HotSpot JVM 中,它获取了一个随机生成的 31 位哈希码,存储在对象的标头中。这必须存储,因为;

  • 即使对象被移动并有了新地址,哈希码也不会改变。
  • 地址不够随机。地址的低 8 位始终为 0。每次 GC 后,要创建的第一个对象始终相同。
  • 地址可以是 64 位。

在家里试试这个,不适合工作!!。

您可以使用Unsafe 获取/设置 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);
    }
}

public static void setIdentityHashCode(Object o, int code) {
    UNSAFE.putInt(o, 1l, code & 0x7FFF_FFF);
}

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Double d = 1.0;
    Double d2 = 1.0;
    setIdentityHashCode(d, 1);
    setIdentityHashCode(d2, 1);
    System.out.println("d: "+d+" System.identityHashCode(d): "+System.identityHashCode(d));
    System.out.println("d2: "+d2+" System.identityHashCode(d2): "+System.identityHashCode(d2));
    System.out.println("d == d2: " + (d == d2));
}

打印

d: 1.0 System.identityHashCode(d): 1
d2: 1.0 System.identityHashCode(d2): 1
d == d2: false

只要您知道内存是如何转换的,您就可以从参考值中获取地址。在最简单的情况下,(您有 64 位引用)引用是未翻译的,地址是存储在引用中的值。

如果你在 64 位 JVM 上使用 -XX:-UseCompressedOops 运行它

// This only works if a GC doesn't move the object while attempting to access it.
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);
    }
}

// run with: -ea -XX:-UseCompressedOops
public static void main(String[] args) {
    Object i = 0x12345678;
    System.out.printf("indentityHashCode = %08x%n", System.identityHashCode(i));

    Object[] obj = { i };
    assert Unsafe.ARRAY_OBJECT_INDEX_SCALE == 8; // 8 bytes per reference.
    long address = UNSAFE.getLong(obj, (long) Unsafe.ARRAY_OBJECT_BASE_OFFSET);
    System.out.printf("%x%n", address);
    for (int j=0;j<24;j++)
        System.out.printf("%02x ", UNSAFE.getByte(address + j) & 0xFF);
    System.out.println();
    // now some really scary sh!t
    UNSAFE.putLong(i, 8L, UNSAFE.getLong(0L, 8L));
    System.out.printf("`i` is now a %s and is %x%n", i.getClass(), i);
}

打印

indentityHashCode = 5a07e868
7fbf41cb8560
01 68 e8 07 5a 00 00 00 48 33 3f b9 b9 7f 00 00 78 56 34 12 00 00 00 00 
   ^^hashCode^          ^class address  ^       ^int value^
`i` is now a class java.lang.Long and is 12345678

【讨论】:

  • 不要告诉别人,他们真的会尝试:D
  • @noctarius 添加了 NSFW 警告。
【解决方案3】:

它没有。它得到的是哈希码,而不是内存地址,而且哈希码与内存地址没有必要的联系。 (它可能在一些实现中。)

在基本实现中,它是System.identityHashCode,尽管它与内存地址仍然没有关系。

【讨论】:

  • hashCode 是 HotSpot 中的一个随机数(默认)
  • 那么我该如何模仿Object.toString()的实现呢?
  • @4castle 整个实现是getClass().getName() + '@' + Integer.toHexString(hashCode())。这就是整个实现。但这与内存地址无关。
  • @Louis 好的,如果假设 hashCode() 已被超类覆盖,我该如何模仿/获取它?
  • @4castle 就是System.identityHashCode(object),不过,这与内存地址无关。
【解决方案4】:

如果您可以在Object 类中找到此toString() 的定义。你会发现这个。

getClass().getName() + '@' + Integer.toHexString(hashCode()) //returns classname@hashCode.

它得到的是hash-code,而不是内存地址(根据Louis Wasserman),并且哈希码与内存地址没有必要的联系。

您应该根据您的需要为您在项目中声明的每个类覆盖此 toString()

【讨论】:

    【解决方案5】:

    这不是获取内存地址的漏洞,因为您得到的只是对象标头中随机生成的 31 位哈希码。它没有提供任何获取真实内存地址的真实方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-16
      • 2013-05-10
      • 2021-12-19
      • 2019-08-26
      • 1970-01-01
      • 2018-09-09
      • 2016-01-21
      相关资源
      最近更新 更多