From 4f123fbe20b2b1c6ada1890c9c33bacaf5033822 Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Sun, 5 Jul 2026 12:21:27 +0200 Subject: [PATCH] Fix #6357: unresolved sticky class GC roots Assisted-by: OpenAI GPT-5 Codex --- .../lib/profiler/heap/HprofGCRoots.java | 5 +++- .../lib/profiler/heap/HeapSegmentTest.java | 24 +++++++++++++++++++ .../netbeans/lib/profiler/heap/HeapUtils.java | 5 ++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/profiler/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoots.java b/profiler/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoots.java index 9da02c8bec32..bb58b5d3626d 100644 --- a/profiler/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoots.java +++ b/profiler/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoots.java @@ -93,7 +93,10 @@ GCRoot getGCRoot(Long instanceId) { heap.getGCRoots(); roots = new HashMap<>(); for (GCRoot r : getGCRoots()) { - roots.put(r.getInstance().getInstanceId(), r); + Instance instance = r.getInstance(); + if (instance != null) { + roots.put(instance.getInstanceId(), r); + } } gcRoots = roots; } else { diff --git a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapSegmentTest.java b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapSegmentTest.java index 87c01062424f..a9353f5e1c0c 100644 --- a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapSegmentTest.java +++ b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapSegmentTest.java @@ -31,6 +31,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.netbeans.lib.profiler.heap.HeapUtils.HprofGenerator; @@ -45,6 +46,29 @@ public void singleObjectMultipleSegments() throws IOException { singleObject(true); } + @Test + public void unresolvedStickyClassRootDoesNotBreakGCRootLookup() throws IOException { + File mydump = File.createTempFile("mydump", ".hprof"); + try (HprofGenerator gen = new HprofGenerator(new FileOutputStream(mydump))) { + gen.writeHeapSegment(new HprofGenerator.Generator() { + @Override + public void generate(HprofGenerator.HeapSegment seg) throws IOException { + seg.newClass("com.oracle.svm.core.heap.heapImpl.DiscoverableReference") + .addField("rawReferent", Object.class) + .dumpClass(); + HprofGenerator.ClassInstance clazz = seg.newClass("text.HelloWorld").dumpClass(); + seg.dumpStickyClassRoot(clazz); + seg.dumpInstance(clazz); + } + }, true); + } + + Heap heap = HeapFactory.createHeap(mydump); + assertEquals("One unresolved sticky class root", 1, heap.getGCRoots().size()); + Instance instance = (Instance) heap.getJavaClassByName("text.HelloWorld").getInstances().iterator().next(); + assertNull("Non-root instance", heap.getGCRoot(instance)); + } + private static void singleObject(boolean flush) throws IOException { File mydump = File.createTempFile("mydump", ".hprof"); Heap heap = generateSampleDump(mydump, flush); diff --git a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapUtils.java b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapUtils.java index 115c99c63832..4ac8e61b9edb 100644 --- a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapUtils.java +++ b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapUtils.java @@ -170,6 +170,11 @@ public int dumpPrimitive(Object obj) throws IOException { return instanceId; } + public void dumpStickyClassRoot(ClassInstance clazz) throws IOException { + heap.writeByte(0x05); + heap.writeInt(clazz.id); + } + public final class ThreadBuilder { private String groupName;