当您在调试器中暂停时,Eclipse 有一个方便的功能,可以显示每个实例的 ID。它们看起来是连续的并且基于分配实例的时间:
当我深入研究一个实质性问题时,我发现这些非常有用,因为它们通常是小整数,比完整的哈希码或其他一些内部 ID 更容易记住。他们会非常快速地检查“这仍然是我认为的那个物体吗?”当您单步执行复杂而深入的调用堆栈时。
这些分配 ID 是由 jvm 本身跟踪的,还是 Eclipse 通过某种未知机制提供的?
有什么方法可以从代码中获取它们,以便我可以在调试时临时将它们放入日志消息中?
我真的不想为每个对象添加 ID 字段,有很多充分的理由说明这是一个坏主意。
这些分配 ID 是由 jvm 本身跟踪的,还是 Eclipse 通过某种未知机制提供的东西?
对象 ID 由 JDWP 代理生成。 JDWP 代理是一个通过 JDWP 协议向外部 Java 调试器(如 Eclipse IDE)公开 JVM 调试功能的库。
代理通常通过命令行选项
-agentlib:jdwp=
或 -Xrunjdwp
与 JVM 一起启动。
它们似乎是连续的并且基于实例的时间 已分配
当代理需要向调试器发送对象引用时,会按需生成 ID。它们与对象创建时间之类的无关。 Java 对象最初没有分配 ID。
有什么方法可以从代码中获取它们
正如我上面所说,在代理与调试器交换对象引用之前,对象 ID 并不存在。
但是您可以使用
IdentityHashMap
或类似工具为对象生成自己的 ID。以下是创建此类 ID 的最简单方法:
private static final Map<Object, Long> map = new IdentityHashMap<>();
private static long nextId;
public static long getId(Object o) {
synchronized (map) {
Long id = map.get(o);
if (id == null) {
map.put(o, id = ++nextId);
}
return id;
}
}
当然,这个实现并不完美,因为它使用全局锁并永久存储对象。更好的解决方案是一种 WeakIdentityConcurrentMap。
事实上,这正是 JDWP 代理生成对象 ID 的方式。它保留一个内部哈希表,将对象映射到顺序递增的整数,并使用引用计数进行删除。请参阅 commonRef.c
作为我在构造函数中使用的唯一 ID
id = System.nanoTime() % 1000;