From 730a4555033765eb29d1f6b95d2bd7bdf8864a03 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Fri, 10 Sep 2021 11:12:58 +0200 Subject: [PATCH 1/2] Eliminate dependency on Swing and call to invokeLater --- .../lib/jfluid/heap/HeapProgress.java | 39 +++--- .../visualvm/lib/jfluid/heap/HprofHeap.java | 30 ++--- .../lib/jfluid/heap/NearestGCRoot.java | 10 +- .../visualvm/lib/jfluid/heap/Progress.java | 116 ++++++++++++++++++ 4 files changed, 159 insertions(+), 36 deletions(-) create mode 100644 visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/Progress.java diff --git a/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/HeapProgress.java b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/HeapProgress.java index cd46127de2..56979c1730 100644 --- a/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/HeapProgress.java +++ b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/HeapProgress.java @@ -37,6 +37,24 @@ public final class HeapProgress { public static final int PROGRESS_MAX = 1000; private static ThreadLocal progressThreadLocal = new ThreadLocal(); + static { + Progress.register(new Progress.Listener() { + @Override + public void started(Progress.Handle h) { + progressStart(); + } + + @Override + public void progress(Progress.Handle h) { + HeapProgress.progress(h.getValue(), h.getEndOffset(), h.getStartOffset()); + } + + @Override + public void finished(Progress.Handle h) { + progressFinish(); + } + }); + } private HeapProgress() { } @@ -51,17 +69,6 @@ public static BoundedRangeModel getProgress() { return info.model; } - static void progress(long counter, long startOffset, long value, long endOffset) { - // keep this method short so that it can be inlined - if (counter % 100000 == 0) { - progress(value, endOffset, startOffset); - } - } - - static void progress(long value, long endValue) { - progress(value,0,value,endValue); - } - private static void progress(final long value, final long endOffset, final long startOffset) { ModelInfo info = progressThreadLocal.get(); if (info != null) { @@ -79,14 +86,14 @@ private static int levelAdd(ModelInfo info, int diff) { return info.level; } - static void progressStart() { + private static void progressStart() { ModelInfo info = progressThreadLocal.get(); if (info != null) { levelAdd(info, 1); } } - static void progressFinish() { + private static void progressFinish() { ModelInfo info = progressThreadLocal.get(); if (info != null) { int level = levelAdd(info, -1); @@ -98,7 +105,7 @@ static void progressFinish() { info.offset = info.model.getValue(); } } - + private static void setValue(final BoundedRangeModel model, final int val) { if (SwingUtilities.isEventDispatchThread()) { model.setValue(val); @@ -108,7 +115,7 @@ private static void setValue(final BoundedRangeModel model, final int val) { }); } } - + private static class ModelInfo { private BoundedRangeModel model; private int level; @@ -117,6 +124,6 @@ private static class ModelInfo { private ModelInfo() { model = new DefaultBoundedRangeModel(0,0,0,PROGRESS_MAX); - } + } } } diff --git a/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/HprofHeap.java b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/HprofHeap.java index 1de494b878..b6bab2a8f5 100644 --- a/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/HprofHeap.java +++ b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/HprofHeap.java @@ -523,7 +523,7 @@ void computeInstances() { return; } - HeapProgress.progressStart(); + Progress.Handle handle = Progress.COMPUTE_INSTANCES.start(); cacheDirectory.setDirty(true); ClassDumpSegment classDumpBounds = getClassDumpSegment(); int idSize = dumpBuffer.getIDSize(); @@ -565,12 +565,12 @@ void computeInstances() { instanceEntry.setIndex(classDump.getInstancesCount()); classDumpBounds.addInstanceSize(classDump, tag, start); } - HeapProgress.progress(counter,allInstanceDumpBounds.startOffset,start,allInstanceDumpBounds.endOffset); + handle.progress(counter,allInstanceDumpBounds.startOffset,start,allInstanceDumpBounds.endOffset); } instancesCountComputed = true; writeToFile(); + handle.close(); } - HeapProgress.progressFinish(); } List findReferencesFor(long instanceId) { @@ -638,7 +638,7 @@ void computeReferences() { return; } - HeapProgress.progressStart(); + Progress.Handle handle = Progress.COMPUTE_INSTANCES.start(); ClassDumpSegment classDumpBounds = getClassDumpSegment(); int idSize = dumpBuffer.getIDSize(); long[] offset = new long[] { allInstanceDumpBounds.startOffset }; @@ -693,7 +693,7 @@ void computeReferences() { } } } - HeapProgress.progress(counter,allInstanceDumpBounds.startOffset,start,allInstanceDumpBounds.endOffset); + handle.progress(counter,allInstanceDumpBounds.startOffset,start,allInstanceDumpBounds.endOffset); } for (JavaClass cls : getClassDumpSegment().createClassCollection()) { @@ -715,8 +715,8 @@ void computeReferences() { idToOffsetMap.flush(); referencesComputed = true; writeToFile(); + handle.close(); } - HeapProgress.progressFinish(); } void computeRetainedSize() { @@ -724,7 +724,7 @@ void computeRetainedSize() { if (retainedSizeComputed) { return; } - HeapProgress.progressStart(); + Progress.Handle handle = Progress.COMPUTE_RETAINED_SIZE.start(); LongBuffer leaves = nearestGCRoot.getLeaves(); cacheDirectory.setDirty(true); new TreeObject(this,leaves).computeTrees(); @@ -779,12 +779,12 @@ void computeRetainedSize() { entry.setRetainedSize(retainedSize+size); } } - HeapProgress.progress(counter,allInstanceDumpBounds.startOffset,start,allInstanceDumpBounds.endOffset); + handle.progress(counter,allInstanceDumpBounds.startOffset,start,allInstanceDumpBounds.endOffset); } retainedSizeComputed = true; writeToFile(); + handle.close(); } - HeapProgress.progressFinish(); } void computeRetainedSizeByClass() { @@ -794,7 +794,7 @@ void computeRetainedSizeByClass() { } computeRetainedSize(); cacheDirectory.setDirty(true); - HeapProgress.progressStart(); + Progress.Handle handle = Progress.COMPUTE_RETAINED_SIZE_BY_CLASS.start(); long[] offset = new long[] { allInstanceDumpBounds.startOffset }; for (long counter=0; offset[0] < allInstanceDumpBounds.endOffset; counter++) { @@ -810,14 +810,14 @@ void computeRetainedSizeByClass() { } } } - HeapProgress.progress(counter,allInstanceDumpBounds.startOffset,start,allInstanceDumpBounds.endOffset); + handle.progress(counter,allInstanceDumpBounds.startOffset,start,allInstanceDumpBounds.endOffset); } // all done, release domTree domTree = null; retainedSizeByClassComputed = true; writeToFile(); + handle.close(); } - HeapProgress.progressFinish(); } Instance getNearestGCRootPointer(Instance instance) { @@ -1283,7 +1283,7 @@ private void fillHeapTagBounds() { return; } - HeapProgress.progressStart(); + Progress.Handle handle = Progress.FILL_HEAP_TAG_BOUNDS.start(); heapTagBounds = new TagBounds[0x100]; long[] offset = new long[] { heapDumpSegment.startOffset + 1 + 4 + 4 }; @@ -1311,7 +1311,7 @@ private void fillHeapTagBounds() { if ((tag == CLASS_DUMP) || (tag == INSTANCE_DUMP) || (tag == OBJECT_ARRAY_DUMP) || (tag == PRIMITIVE_ARRAY_DUMP)) { idMapSize++; } - HeapProgress.progress(counter,heapDumpSegment.startOffset,start,heapDumpSegment.endOffset); + handle.progress(counter,heapDumpSegment.startOffset,start,heapDumpSegment.endOffset); } TagBounds instanceDumpBounds = heapTagBounds[INSTANCE_DUMP]; @@ -1319,7 +1319,7 @@ private void fillHeapTagBounds() { TagBounds primArrayDumpBounds = heapTagBounds[PRIMITIVE_ARRAY_DUMP]; allInstanceDumpBounds = instanceDumpBounds.union(objArrayDumpBounds); allInstanceDumpBounds = allInstanceDumpBounds.union(primArrayDumpBounds); - HeapProgress.progressFinish(); + handle.close(); } private void fillTagBounds(long tagStart) throws IOException { diff --git a/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/NearestGCRoot.java b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/NearestGCRoot.java index 3222b54652..e51f935cab 100644 --- a/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/NearestGCRoot.java +++ b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/NearestGCRoot.java @@ -97,7 +97,7 @@ private synchronized void computeGCRoots() { if (gcRootsComputed) { return; } - HeapProgress.progressStart(); + Progress.Handle handle = Progress.COMPUTE_GC_ROOTS.start(); if (!initHotSpotReference()) { if (!initSVMReference()) { throw new IllegalArgumentException("reference field not found"); // NOI18N @@ -114,7 +114,7 @@ private synchronized void computeGCRoots() { do { switchBuffers(); - computeOneLevel(processedClasses); + computeOneLevel(processedClasses, handle); } while (hasMoreLevels()); } catch (IOException ex) { ex.printStackTrace(); @@ -124,7 +124,7 @@ private synchronized void computeGCRoots() { heap.idToOffsetMap.flush(); gcRootsComputed = true; heap.writeToFile(); - HeapProgress.progressFinish(); + handle.close(); } private boolean initHotSpotReference() { @@ -159,7 +159,7 @@ private boolean initSVMReference() { return false; } - private void computeOneLevel(Set processedClasses) throws IOException { + private void computeOneLevel(Set processedClasses, Progress.Handle handle) throws IOException { int idSize = heap.dumpBuffer.getIDSize(); for (;;) { Instance instance; @@ -170,7 +170,7 @@ private void computeOneLevel(Set processedClasses) throws IOException { if (instanceOffset == 0L) { // end of level break; } - HeapProgress.progress(processedInstances++,allInstances); + handle.progress(processedInstances++,allInstances); instance = heap.getInstanceByOffset(new long[] {instanceOffset}); if (instance instanceof ObjectArrayInstance) { ObjectArrayDump array = (ObjectArrayDump) instance; diff --git a/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/Progress.java b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/Progress.java new file mode 100644 index 0000000000..df10019e8b --- /dev/null +++ b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/Progress.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 1997, 2021, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.visualvm.lib.jfluid.heap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +enum Progress { + COMPUTE_INSTANCES, + COMPUTE_REFERENCES, + FILL_HEAP_TAG_BOUNDS, + COMPUTE_GC_ROOTS, + COMPUTE_RETAINED_SIZE, + COMPUTE_RETAINED_SIZE_BY_CLASS; + + Handle start() { + return new Handle(this); + } + + private static List listeners = Collections.emptyList(); + synchronized static void register(Listener onChange) { + if (listeners.isEmpty()) { + listeners = Collections.singletonList(onChange); + } else { + List copy = new ArrayList<>(listeners); + copy.add(onChange); + listeners = copy; + } + } + + synchronized static void notifyUpdates(Handle h, int type) { + for (Listener onChange : listeners) { + switch (type) { + case 1: onChange.started(h); break; + case 2: onChange.progress(h); break; + default: onChange.finished(h); + } + } + } + + static interface Listener { + void started(Handle h); + void progress(Handle h); + void finished(Handle h); + } + + static final class Handle implements AutoCloseable { + final Progress type; + private long value; + private long startOffset; + private long endOffset; + + private Handle(Progress type) { + this.type = type; + notifyUpdates(this, 1); + } + + void progress(long value, long endValue) { + progress(value, 0, value, endValue); + } + + void progress(long counter, long startOffset, long value, long endOffset) { + // keep this method short so that it can be inlined + if (counter % 100000 == 0) { + doProgress(value, startOffset, endOffset); + } + } + + @Override + public void close() { + notifyUpdates(this, 2); + } + + private void doProgress(long value, long startOffset, long endOffset) { + this.value = value; + this.endOffset = endOffset; + this.startOffset = startOffset; + notifyUpdates(this, 1); + } + + long getValue() { + return value; + } + + long getStartOffset() { + return startOffset; + } + + long getEndOffset() { + return endOffset; + } + } +} From 14807e87906e6503f9466d202c779e1d9476aebc Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Mon, 13 Sep 2021 09:40:19 +0200 Subject: [PATCH 2/2] Using enum to classify types of progress events --- .../visualvm/lib/jfluid/heap/Progress.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/Progress.java b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/Progress.java index df10019e8b..fac7298c7f 100644 --- a/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/Progress.java +++ b/visualvm/libs.profiler/lib.profiler/src/org/graalvm/visualvm/lib/jfluid/heap/Progress.java @@ -51,16 +51,20 @@ synchronized static void register(Listener onChange) { } } - synchronized static void notifyUpdates(Handle h, int type) { + private synchronized static void notifyUpdates(Handle h, Type type) { for (Listener onChange : listeners) { switch (type) { - case 1: onChange.started(h); break; - case 2: onChange.progress(h); break; + case STARTED: onChange.started(h); break; + case PROGRESS: onChange.progress(h); break; default: onChange.finished(h); } } } + private enum Type { + STARTED, PROGRESS, FINISHED; + } + static interface Listener { void started(Handle h); void progress(Handle h); @@ -75,7 +79,7 @@ static final class Handle implements AutoCloseable { private Handle(Progress type) { this.type = type; - notifyUpdates(this, 1); + notifyUpdates(this, Type.STARTED); } void progress(long value, long endValue) { @@ -91,14 +95,14 @@ void progress(long counter, long startOffset, long value, long endOffset) { @Override public void close() { - notifyUpdates(this, 2); + notifyUpdates(this, Type.FINISHED); } private void doProgress(long value, long startOffset, long endOffset) { this.value = value; this.endOffset = endOffset; this.startOffset = startOffset; - notifyUpdates(this, 1); + notifyUpdates(this, Type.PROGRESS); } long getValue() {