From f25d429c8d6d099666aefd698ed14628cce5b1cf Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Tue, 24 Feb 2026 03:01:19 +0000 Subject: [PATCH] 8306591: SA and hotspot generate different hprof records for GC roots Reviewed-by: cjplummer, dholmes --- src/hotspot/share/runtime/vmStructs.cpp | 2 ++ .../hotspot/classfile/ClassLoaderData.java | 8 +++++++- .../utilities/AbstractHeapGraphWriter.java | 8 +++++++- .../hotspot/utilities/HeapHprofBinWriter.java | 20 +++++++++++++++++++ .../serviceability/sa/ClhsdbDumpheap.java | 13 ++++++++++++ 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 9ac8f396f9a..a54fbc7e8ab 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -408,6 +408,8 @@ volatile_nonstatic_field(ClassLoaderData, _klasses, Klass*) \ nonstatic_field(ClassLoaderData, _has_class_mirror_holder, bool) \ \ + static_field(ClassLoaderData, _the_null_class_loader_data, ClassLoaderData*) \ + \ volatile_static_field(ClassLoaderDataGraph, _head, ClassLoaderData*) \ \ /**********/ \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/classfile/ClassLoaderData.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/classfile/ClassLoaderData.java index 44b407edaca..7ce46799495 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/classfile/ClassLoaderData.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/classfile/ClassLoaderData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,12 +44,14 @@ public class ClassLoaderData extends VMObject { private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { Type type = db.lookupType("ClassLoaderData"); classLoaderFieldOffset = type.getAddressField("_class_loader").getOffset(); + theNullClassLoaderDataField = type.getAddressField("_the_null_class_loader_data"); nextField = type.getAddressField("_next"); klassesField = new MetadataField(type.getAddressField("_klasses"), 0); hasClassMirrorHolderField = new CIntField(type.getCIntegerField("_has_class_mirror_holder"), 0); } private static long classLoaderFieldOffset; + private static AddressField theNullClassLoaderDataField; private static AddressField nextField; private static MetadataField klassesField; private static CIntField hasClassMirrorHolderField; @@ -75,6 +77,10 @@ public class ClassLoaderData extends VMObject { return hasClassMirrorHolderField.getValue(this) != 0; } + public static ClassLoaderData theNullClassLoaderData() { + return instantiateWrapperFor(theNullClassLoaderDataField.getValue()); + } + public ClassLoaderData next() { return instantiateWrapperFor(nextField.getValue(getAddress())); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java index 51f40fad12b..80ea272ea2c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -121,6 +121,9 @@ public abstract class AbstractHeapGraphWriter implements HeapGraphWriter { // write JNI global handles writeGlobalJNIHandles(); + // write classes in null class loader data + writeStickyClasses(); + } catch (RuntimeException re) { handleRuntimeException(re); } @@ -168,6 +171,9 @@ public abstract class AbstractHeapGraphWriter implements HeapGraphWriter { } } + protected void writeStickyClasses() throws IOException { + } + protected void writeGlobalJNIHandle(Address handleAddr) throws IOException { } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java index ce048bf2d86..2745e1f0783 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java @@ -967,6 +967,22 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { } } + @Override + protected void writeStickyClasses() throws IOException { + ClassLoaderData.theNullClassLoaderData().classesDo(k -> { + if (k instanceof InstanceKlass) { + try { + int size = 1 + (int)VM.getVM().getAddressSize(); + writeHeapRecordPrologue(size); + out.writeByte((byte)HPROF_GC_ROOT_STICKY_CLASS); + writeClassID(k); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + }); + } + protected void writeObjectArray(ObjArray array) throws IOException { int headerSize = getArrayHeaderSize(true); final int length = calculateArrayMaxLength(array.getLength(), @@ -1304,6 +1320,10 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { writeObjectID(address); } + private void writeClassID(Klass k) throws IOException { + writeObjectID(k.getJavaMirror()); + } + private void writeSymbolID(Symbol sym) throws IOException { assert names.contains(sym); long address = (sym != null) ? getAddressValue(sym.getAddress()) : getAddressValue(null); diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java index 722c641f5b9..d9f856ab0a3 100644 --- a/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java @@ -81,10 +81,23 @@ public class ClhsdbDumpheap { fail("HPROF_GC_ROOT_JAVA_FRAME not found"); } + private static void verifyStickyClasses(String file) throws IOException { + try (var snapshot = HprofReader.readFile(file, false, 0)) { + for (var root = snapshot.getRoots(); root.hasMoreElements();) { + if (root.nextElement().getType() == Root.SYSTEM_CLASS) { + // expected + return; + } + } + } + fail("HPROF_GC_ROOT_STICKY_CLASS not found"); + } + private static void verifyDumpFile(File dump) throws Exception { assertTrue(dump.exists() && dump.isFile(), "Could not create dump file " + dump.getAbsolutePath()); printStackTraces(dump.getAbsolutePath()); verifyLocalRefs(dump.getAbsolutePath()); + verifyStickyClasses(dump.getAbsolutePath()); } private static class SubTest {