如何找出确切的年轻/老年人在记忆中的位置?

最近我能够使用sun.misc.Unsafe类获取对象的地址。

现在我试图以编程方式找到我的对象所在的实际生成。 为此,我想知道每一代的起点和终点。 如果Java(Oracle JVM)提供了解决此问题的任何工具? 我不相信,因为即使不同的GC需要不同的内存结构(例如G1),这使得任务更加有趣:)

我想知道的只是代表内存中几代边界的几个数字,如下所示:

young gen: start point - 8501702198 end point - 9601256348 

愿意听到关于黑魔法的最疯狂的想法,这些想法可以确定不同代区域在记忆中的位置。

HotSpot JVM虽然有些复杂,但这是可行的。

关键思想是使用VMStructs – 有关嵌入JVM共享库的HotSpot内部常量和类型的信息。

例如, ParallelScavengeHeap::_young_gen VM全局变量包含指向PSYoungGen结构的指针, PSYoungGen结构具有_virtual_space成员,其边界为Parallel collector的年轻代。 同样, GenCollectedHeap::_gch全局指向描述CMS收集器代的结构。

我做了一个概念validation项目来演示VMStructs的用法。 它是纯Java,不需要额外的库,但它非常依赖于未记录的JDK内部,并且可能不适用于所有Java版本。 我已经在Windows和Linux上对JDK 8u40和JDK 7u80进行了测试。

  • JVM.java – 读取VMStructs的代码;
  • HeapInfo.java – 获取Heap代数地址的示例程序。

我不知道如何准确地获得年轻一代的边界(甚至不确定它是否可能)。 在G1的情况下它变得更加复杂,因为由于G1的不寻常的堆结构,它允许具有多个旧的一代区域。

但是,如果不了解世代的边界,您可以使用棘手的启发式方法来确定对象是否属于旧一代。

让我们使用一些关于热点内部的 黑魔法秘密知识:每个对象都包含头部,其中包含有关锁定,身份哈希码以及最重要的年龄的所有必要信息。 提取年龄将如下所示:

 return unsafe.getByte(targetObject, 0L) & 0x78; 

其中0x78是对象标题中与其年龄相对应的掩码(从第4位到第7位)。

通过Management API获取MaxTenuringThreshold参数:

 MBeanServer server = ManagementFactory.getPlatformMBeanServer(); HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy( server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class); int threshold = Integer.valueOf(bean.getVMOption("MaxTenuringThreshold").getValue()); 

现在您知道对象的年龄和应用程序的期限阈值,因此您可以假设如果年龄大于阈值,那么它就属于旧一代。

警告:它是基于魔法和秘密知识的启发式算法。

  1. 如果有人会在读取它的年龄时同步目标对象,它将无法工作,因为VM会将标头移动到堆栈并用指针替换标头到堆栈
  2. 它不适用于XX:+UseAdaptiveSizePolicy ,因此您应该明确禁用它。
  3. 有些对象可以在老一代直接分配(例如因为它的大小)
  4. 类型I容易出错
  5. 这种方法是非法的,不安全的,可能是不正确的,并且依赖于jvm