如何找出确切的年轻/老年人在记忆中的位置?
最近我能够使用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());
现在您知道对象的年龄和应用程序的期限阈值,因此您可以假设如果年龄大于阈值,那么它就属于旧一代。
警告:它是基于魔法和秘密知识的启发式算法。
- 如果有人会在读取它的年龄时同步目标对象,它将无法工作,因为VM会将标头移动到堆栈并用指针替换标头到堆栈
- 它不适用于
XX:+UseAdaptiveSizePolicy
,因此您应该明确禁用它。 - 有些对象可以在老一代直接分配(例如因为它的大小)
- 类型I容易出错
- 这种方法是非法的,不安全的,可能是不正确的,并且依赖于jvm