From 1923bf4c54ac2f05de02e4c7e8c6343765578961 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Tue, 1 Oct 2024 12:21:33 -0400 Subject: [PATCH 01/12] GC notifications --- substratevm/mx.substratevm/suite.py | 6 + .../AbstractGarbageCollectorMXBean.java | 134 +++++++++++++++ .../genscavenge/AbstractMemoryPoolMXBean.java | 14 +- .../CompleteGarbageCollectorMXBean.java | 29 +--- .../oracle/svm/core/genscavenge/GCImpl.java | 9 + .../GenScavengeMemoryPoolMXBeans.java | 33 +++- .../svm/core/genscavenge/HeapAccounting.java | 2 +- .../IncrementalGarbageCollectorMXBean.java | 29 +--- .../service/GcNotificationRequest.java | 104 ++++++++++++ .../core/genscavenge/service/GcNotifier.java | 159 ++++++++++++++++++ .../service/HasGcNotificationSupport.java | 38 +++++ .../genscavenge/service/PoolMemoryUsage.java | 34 ++++ .../genscavenge/service/ServiceFeature.java | 65 +++++++ .../genscavenge/service/ServiceSupport.java | 79 +++++++++ .../genscavenge/service/ServiceThread.java | 87 ++++++++++ .../Target_com_sun_management_GcInfo.java | 47 ++++++ ...sun_management_internal_GcInfoBuilder.java | 64 +++++++ .../com/oracle/svm/core/SubstrateOptions.java | 3 +- .../oracle/svm/core/VMInspectionOptions.java | 11 +- .../native-image.properties | 2 +- .../svm/test/service/GcNotificationTests.java | 122 ++++++++++++++ 21 files changed, 1002 insertions(+), 69 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/PoolMemoryUsage.java create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceFeature.java create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceSupport.java create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceThread.java create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_internal_GcInfoBuilder.java create mode 100644 substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 453d2eafb672..7e00945c157f 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -356,6 +356,9 @@ "java.management": [ "sun.management", ], + "jdk.management": [ + "com.sun.management.internal" + ], "jdk.internal.vm.ci" : [ "jdk.vm.ci.code", ], @@ -994,6 +997,9 @@ "jdk.internal.misc", "sun.security.jca", ], + "java.management": [ + "sun.management", + ], }, "checkstyle": "com.oracle.svm.test", "checkstyleVersion" : "10.7.0", diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java new file mode 100644 index 000000000000..228043840189 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge; + +import com.oracle.svm.core.util.BasedOnJDKFile; + +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.genscavenge.service.GcNotificationRequest; +import com.oracle.svm.core.genscavenge.service.PoolMemoryUsage; +import com.oracle.svm.core.genscavenge.service.Target_com_sun_management_GcInfo; +import com.oracle.svm.core.genscavenge.service.Target_com_sun_management_internal_GcInfoBuilder; + +import com.sun.management.GarbageCollectionNotificationInfo; +import com.sun.management.GcInfo; +import com.sun.management.internal.GarbageCollectionNotifInfoCompositeData; +import com.sun.management.internal.GcInfoBuilder; +import sun.management.NotificationEmitterSupport; + +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.openmbean.CompositeData; +import java.lang.management.MemoryUsage; + +public abstract class AbstractGarbageCollectorMXBean extends NotificationEmitterSupport + implements com.sun.management.GarbageCollectorMXBean, NotificationEmitter { + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/hotspot/share/gc/serial/serialHeap.cpp#L451") // + private static final String ACTION_MINOR = "end of minor GC"; + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/hotspot/share/gc/serial/serialHeap.cpp#L718") // + private static final String ACTION_MAJOR = "end of major GC"; + + private GcInfoBuilder gcInfoBuilder; + private volatile GcInfo gcInfo; + private long seqNumber = 0; + + private synchronized GcInfoBuilder getGcInfoBuilder() { + if (gcInfoBuilder == null) { + Target_com_sun_management_internal_GcInfoBuilder gib = new Target_com_sun_management_internal_GcInfoBuilder(this, getMemoryPoolNames()); + gcInfoBuilder = SubstrateUtil.cast(gib, GcInfoBuilder.class); + } + return gcInfoBuilder; + } + + /** + * Use the data taken from the request queue to populate MemoryUsage. The service thread calls + * this method. + */ + public void createNotification(GcNotificationRequest request) { + AbstractMemoryPoolMXBean[] beans = GenScavengeMemoryPoolMXBeans.singleton().getMXBeans(); + + String[] poolNames = getMemoryPoolNames(); + MemoryUsage[] before = new MemoryUsage[poolNames.length]; + MemoryUsage[] after = new MemoryUsage[poolNames.length]; + + // Pools must be in the order of getMemoryPoolNames() to match GcInfoBuilder + for (int i = 0; i < poolNames.length; i++) { + for (int j = 0; j < beans.length; j++) { + PoolMemoryUsage pmu = request.getPoolBefore(j); + if (pmu.name != null && pmu.name.equals(poolNames[i])) { + before[i] = beans[j].memoryUsage(pmu.used, pmu.committed); + pmu = request.getPoolAfter(j); + after[i] = beans[j].memoryUsage(pmu.used, pmu.committed); + } + } + } + + // Number of GC threads. + Object[] extAttribute = new Object[]{Integer.valueOf(1)}; + + Target_com_sun_management_GcInfo targetGcInfo = new Target_com_sun_management_GcInfo(getGcInfoBuilder(), request.epoch, request.startTime, request.endTime, before, after, extAttribute); + gcInfo = SubstrateUtil.cast(targetGcInfo, GcInfo.class); + + GarbageCollectionNotificationInfo info = new GarbageCollectionNotificationInfo( + getName(), + request.isIncremental ? ACTION_MINOR : ACTION_MAJOR, + request.cause.getName(), + gcInfo); + + CompositeData cd = GarbageCollectionNotifInfoCompositeData.toCompositeData(info); + + Notification notif = new Notification(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION, + getObjectName(), + getNextSeqNumber(), + request.timestamp, + getName()); + notif.setUserData(cd); + + sendNotification(notif); + } + + private long getNextSeqNumber() { + return ++seqNumber; + } + + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + return new MBeanNotificationInfo[]{ + new MBeanNotificationInfo( + new String[]{GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION}, + "javax.management.Notification", + "GC Notification") + }; + } + + @Override + public GcInfo getLastGcInfo() { + return gcInfo; + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractMemoryPoolMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractMemoryPoolMXBean.java index d0724fd7b597..88b4718e5ccd 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractMemoryPoolMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractMemoryPoolMXBean.java @@ -66,6 +66,12 @@ UnsignedWord getInitialValue() { return initialValue; } + public abstract UnsignedWord getUsedBytes(); + + public UnsignedWord getCommittedBytes() { + return getUsedBytes(); + } + abstract UnsignedWord computeInitialValue(); abstract void beforeCollection(); @@ -73,11 +79,15 @@ UnsignedWord getInitialValue() { abstract void afterCollection(); MemoryUsage memoryUsage(UnsignedWord usedAndCommitted) { - return new MemoryUsage(getInitialValue().rawValue(), usedAndCommitted.rawValue(), usedAndCommitted.rawValue(), getMaximumValue().rawValue()); + return memoryUsage(usedAndCommitted, usedAndCommitted); } MemoryUsage memoryUsage(UnsignedWord used, UnsignedWord committed) { - return new MemoryUsage(getInitialValue().rawValue(), used.rawValue(), committed.rawValue(), getMaximumValue().rawValue()); + return memoryUsage(used.rawValue(), committed.rawValue()); + } + + public MemoryUsage memoryUsage(long used, long committed) { + return new MemoryUsage(getInitialValue().rawValue(), used, committed, getMaximumValue().rawValue()); } @Override diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java index c94f2f39d819..0355a26ed94d 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java @@ -26,21 +26,16 @@ import java.lang.management.ManagementFactory; -import javax.management.MBeanNotificationInfo; -import javax.management.NotificationEmitter; -import javax.management.NotificationFilter; -import javax.management.NotificationListener; import javax.management.ObjectName; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.util.TimeUtils; -import com.sun.management.GcInfo; import sun.management.Util; -public final class CompleteGarbageCollectorMXBean implements com.sun.management.GarbageCollectorMXBean, NotificationEmitter { +public final class CompleteGarbageCollectorMXBean extends AbstractGarbageCollectorMXBean { @Platforms(Platform.HOSTED_ONLY.class) public CompleteGarbageCollectorMXBean() { @@ -81,26 +76,4 @@ public boolean isValid() { public ObjectName getObjectName() { return Util.newObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE, getName()); } - - @Override - public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) { - } - - @Override - public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) { - } - - @Override - public void removeNotificationListener(NotificationListener listener) { - } - - @Override - public MBeanNotificationInfo[] getNotificationInfo() { - return new MBeanNotificationInfo[0]; - } - - @Override - public GcInfo getLastGcInfo() { - return null; - } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java index df8ca685efbb..3392de746964 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java @@ -66,6 +66,7 @@ import com.oracle.svm.core.genscavenge.HeapChunk.Header; import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; import com.oracle.svm.core.genscavenge.remset.RememberedSet; +import com.oracle.svm.core.genscavenge.service.ServiceSupport; import com.oracle.svm.core.graal.RuntimeCompilation; import com.oracle.svm.core.heap.CodeReferenceMapDecoder; import com.oracle.svm.core.heap.GC; @@ -103,6 +104,7 @@ import com.oracle.svm.core.threadlocal.VMThreadLocalSupport; import com.oracle.svm.core.util.TimeUtils; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.core.VMInspectionOptions; import jdk.graal.compiler.api.replacements.Fold; @@ -240,6 +242,9 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) || try { ThreadLocalAllocation.disableAndFlushForAllThreads(); GenScavengeMemoryPoolMXBeans.singleton().notifyBeforeCollection(); + if (VMInspectionOptions.hasGcNotificationSupport()) { + ServiceSupport.singleton().beforeCollection(Isolates.getCurrentUptimeMillis()); + } HeapImpl.getAccounting().notifyBeforeCollection(); verifyHeap(Before); @@ -257,6 +262,10 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) || GenScavengeMemoryPoolMXBeans.singleton().notifyAfterCollection(); ChunkBasedCommittedMemoryProvider.get().afterGarbageCollection(); + if (VMInspectionOptions.hasGcNotificationSupport()) { + ServiceSupport.singleton().afterCollection(!isCompleteCollection(), cause, getCollectionEpoch(), Isolates.getCurrentUptimeMillis()); + } + printGCAfter(cause); JfrGCHeapSummaryEvent.emit(JfrGCWhen.AFTER_GC); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java index 093e7e9e3796..65a83734704f 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java @@ -39,13 +39,13 @@ import jdk.graal.compiler.api.replacements.Fold; public class GenScavengeMemoryPoolMXBeans { - static final String YOUNG_GEN_SCAVENGER = "young generation scavenger"; - static final String COMPLETE_SCAVENGER = "complete scavenger"; + public static final String YOUNG_GEN_SCAVENGER = "young generation scavenger"; + public static final String COMPLETE_SCAVENGER = "complete scavenger"; static final String EPSILON_SCAVENGER = "epsilon scavenger"; static final String EDEN_SPACE = "eden space"; static final String SURVIVOR_SPACE = "survivor space"; - static final String OLD_GEN_SPACE = "old generation space"; + public static final String OLD_GEN_SPACE = "old generation space"; static final String EPSILON_HEAP = "epsilon heap"; private final AbstractMemoryPoolMXBean[] mxBeans; @@ -111,12 +111,12 @@ UnsignedWord computeInitialValue() { @Override public MemoryUsage getUsage() { - return memoryUsage(getCurrentUsage()); + return memoryUsage(getUsedBytes()); } @Override public MemoryUsage getPeakUsage() { - updatePeakUsage(getCurrentUsage()); + updatePeakUsage(getUsedBytes()); return memoryUsage(peakUsage.get()); } @@ -125,7 +125,8 @@ public MemoryUsage getCollectionUsage() { return memoryUsage(WordFactory.zero()); } - private static UnsignedWord getCurrentUsage() { + @Override + public UnsignedWord getUsedBytes() { return HeapImpl.getAccounting().getEdenUsedBytes(); } } @@ -166,6 +167,11 @@ public MemoryUsage getPeakUsage() { public MemoryUsage getCollectionUsage() { return memoryUsage(HeapImpl.getAccounting().getSurvivorUsedBytes()); } + + @Override + public UnsignedWord getUsedBytes() { + return HeapImpl.getAccounting().getSurvivorUsedBytes(); + } } static final class OldGenerationMemoryPoolMXBean extends AbstractMemoryPoolMXBean { @@ -204,6 +210,11 @@ public MemoryUsage getPeakUsage() { public MemoryUsage getCollectionUsage() { return memoryUsage(HeapImpl.getAccounting().getOldUsedBytes()); } + + @Override + public UnsignedWord getUsedBytes() { + return HeapImpl.getAccounting().getOldUsedBytes(); + } } static final class EpsilonMemoryPoolMXBean extends AbstractMemoryPoolMXBean { @@ -243,5 +254,15 @@ public MemoryUsage getPeakUsage() { public MemoryUsage getCollectionUsage() { return memoryUsage(WordFactory.zero()); } + + @Override + public UnsignedWord getUsedBytes() { + return HeapImpl.getAccounting().getUsedBytes(); + } + + @Override + public UnsignedWord getCommittedBytes() { + return HeapImpl.getAccounting().getCommittedBytes(); + } } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapAccounting.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapAccounting.java index e1b3153abcce..523e28f6cc2c 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapAccounting.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapAccounting.java @@ -79,7 +79,7 @@ public void notifyAfterCollection() { invalidData = false; } - HeapSizes getHeapSizesBeforeGc() { + public HeapSizes getHeapSizesBeforeGc() { return beforeGc; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java index 38f7305c177f..e5800892f84e 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java @@ -26,21 +26,16 @@ import java.lang.management.ManagementFactory; -import javax.management.MBeanNotificationInfo; -import javax.management.NotificationEmitter; -import javax.management.NotificationFilter; -import javax.management.NotificationListener; import javax.management.ObjectName; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.util.TimeUtils; -import com.sun.management.GcInfo; import sun.management.Util; -public final class IncrementalGarbageCollectorMXBean implements com.sun.management.GarbageCollectorMXBean, NotificationEmitter { +public final class IncrementalGarbageCollectorMXBean extends AbstractGarbageCollectorMXBean { @Platforms(Platform.HOSTED_ONLY.class) public IncrementalGarbageCollectorMXBean() { @@ -80,26 +75,4 @@ public boolean isValid() { public ObjectName getObjectName() { return Util.newObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE, getName()); } - - @Override - public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) { - } - - @Override - public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) { - } - - @Override - public void removeNotificationListener(NotificationListener listener) { - } - - @Override - public MBeanNotificationInfo[] getNotificationInfo() { - return new MBeanNotificationInfo[0]; - } - - @Override - public GcInfo getLastGcInfo() { - return null; - } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java new file mode 100644 index 000000000000..64abd9ec5414 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +import com.oracle.svm.core.heap.GCCause; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.core.Uninterruptible; + +/** + * This class stashes GC info during a safepoint until it can be dequeued to emit a notification at + * a point in the future. + */ +public class GcNotificationRequest { + private PoolMemoryUsage[] before; + private PoolMemoryUsage[] after; + + // Times since the VM started. + public long startTime; + public long endTime; + + // This is the system time that the request was sent. + public long timestamp; + public boolean isIncremental; + public GCCause cause; + public long epoch; + + @Platforms(Platform.HOSTED_ONLY.class) + GcNotificationRequest(int poolCount) { + before = new PoolMemoryUsage[poolCount]; + after = new PoolMemoryUsage[poolCount]; + for (int i = 0; i < poolCount; i++) { + before[i] = new PoolMemoryUsage(); + after[i] = new PoolMemoryUsage(); + } + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + void setPoolBefore(int index, long used, long committed, String name) { + before[index].used = used; + before[index].committed = committed; + before[index].name = name; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + void setPoolAfter(int index, long used, long committed, String name) { + after[index].used = used; + after[index].committed = committed; + after[index].name = name; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public PoolMemoryUsage getPoolBefore(int i) { + return before[i]; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public PoolMemoryUsage getPoolAfter(int i) { + return after[i]; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + void copyTo(GcNotificationRequest other) { + other.startTime = startTime; + other.epoch = epoch; + other.endTime = endTime; + other.cause = cause; + other.timestamp = timestamp; + other.isIncremental = isIncremental; + + for (int index = 0; index < before.length; index++) { + other.setPoolBefore(index, before[index].used, before[index].committed, before[index].name); + } + + for (int index = 0; index < after.length; index++) { + other.setPoolAfter(index, after[index].used, after[index].committed, after[index].name); + } + } + +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java new file mode 100644 index 000000000000..b5cbd0e6f681 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.Platform; +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.genscavenge.AbstractMemoryPoolMXBean; +import com.oracle.svm.core.heap.GCCause; +import com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans; +import com.oracle.svm.core.genscavenge.AbstractGarbageCollectorMXBean; +import com.oracle.svm.core.thread.VMOperation; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; + +import static com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans.COMPLETE_SCAVENGER; +import static com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans.OLD_GEN_SPACE; +import static com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans.YOUNG_GEN_SCAVENGER; + +/** + * This class keeps a circular queue of requests to later use when emitting GC notifications. The + * requests are pre-allocated so that they can be used during a GC. + * + * This class is responsible for enqueuing requests during GC safepoints and later dequeuing + * requests to generate notifications when the service thread handles signals outside of safepoints. + */ +public class GcNotifier { + // 5 is probably more than enough, although making the queue larger isn't a problem either. + private static final int QUEUE_SIZE = 5; + private static volatile int rear; + private static volatile int front; + + GcNotificationRequest[] requests; + GcNotificationRequest currentRequest; + + @Platforms(Platform.HOSTED_ONLY.class) + GcNotifier() { + // Pre-allocate the queue so we can use it later from allocation free code. + requests = new GcNotificationRequest[QUEUE_SIZE]; + for (int i = 0; i < QUEUE_SIZE; i++) { + requests[i] = new GcNotificationRequest(GenScavengeMemoryPoolMXBeans.singleton().getMXBeans().length); + } + currentRequest = new GcNotificationRequest(GenScavengeMemoryPoolMXBeans.singleton().getMXBeans().length); + rear = 0; + front = 0; + } + + /** Called during GC. */ + void beforeCollection(long startTime) { + assert VMOperation.isInProgressAtSafepoint(); + + AbstractMemoryPoolMXBean[] beans = GenScavengeMemoryPoolMXBeans.singleton().getMXBeans(); + for (int i = 0; i < beans.length; i++) { + /* + * Always add the old generation pool even if we don't end up using it (since we don't + * know what type of collection will happen yet) + */ + requests[rear].setPoolBefore(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); + } + requests[rear].startTime = startTime; + } + + /** Called during GC. */ + void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime) { + assert VMOperation.isInProgressAtSafepoint(); + + AbstractMemoryPoolMXBean[] beans = GenScavengeMemoryPoolMXBeans.singleton().getMXBeans(); + for (int i = 0; i < beans.length; i++) { + if (isIncremental && beans[i].getName().equals(OLD_GEN_SPACE)) { + continue; + } + requests[rear].setPoolAfter(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); + } + requests[rear].endTime = endTime; + requests[rear].isIncremental = isIncremental; + requests[rear].cause = cause; + requests[rear].epoch = epoch; + requests[rear].timestamp = System.currentTimeMillis(); + + rear = incremented(rear); + + // If rear is now lapping front, then increment front to prevent overlap. Accept data loss. + if (front == rear) { + front = incremented(front); + } + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private static int incremented(int value) { + return (value + 1) % QUEUE_SIZE; + } + + /** + * Called by the service thread. Sends a notification if any are queued. If none are queued, + * then return immediately. + */ + public void sendNotification() { + assert !VMOperation.isInProgressAtSafepoint(); + + /* + * We don't need to drain all requests since signals queue at the semaphore level. The rest + * of the requests will be handled in subsequent calls to this method by the sevice thread. + */ + updateCurrentRequest(); + + /* + * Iterate to figure out which gc bean to send notifications too. Match with name in + * NotificationRequest class that was dequeued. + */ + GarbageCollectorMXBean gcBean = null; + for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { + if ((currentRequest.isIncremental && bean.getName().equals(YOUNG_GEN_SCAVENGER)) || (!currentRequest.isIncremental && bean.getName().equals(COMPLETE_SCAVENGER))) { + gcBean = bean; + break; + } + } + + assert (gcBean != null); + ((AbstractGarbageCollectorMXBean) gcBean).createNotification(currentRequest); + } + + /** Called by the service thread. */ + @Uninterruptible(reason = "Avoid pausing for a GC safepoint with could cause races with pushes to the request queue.") + private boolean updateCurrentRequest() { + // If there's nothing to read, return immediately. + if (front == rear) { + return false; + } + // Copy the data to avoid data being overwritten by new writes to the queue. + requests[front].copyTo(currentRequest); + front = incremented(front); + return true; + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java new file mode 100644 index 000000000000..95fa6d9f8235 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +import java.util.function.BooleanSupplier; + +import com.oracle.svm.core.VMInspectionOptions; + +public class HasGcNotificationSupport implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return VMInspectionOptions.hasGcNotificationSupport(); + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/PoolMemoryUsage.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/PoolMemoryUsage.java new file mode 100644 index 000000000000..6d630c6213a6 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/PoolMemoryUsage.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +/** Used from allocation free code and later converted to MemoryUsage. */ +public class PoolMemoryUsage { + public long used; + public long committed; + public String name; +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceFeature.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceFeature.java new file mode 100644 index 000000000000..82f54a2147d2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceFeature.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.jdk.RuntimeSupport; +import com.oracle.svm.core.VMInspectionOptions; +import org.graalvm.nativeimage.ImageSingletons; + +@AutomaticallyRegisteredFeature +public class ServiceFeature implements InternalFeature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return VMInspectionOptions.hasGcNotificationSupport(); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + RuntimeSupport.getRuntimeSupport().addStartupHook(new ServiceStartupHook()); + RuntimeSupport.getRuntimeSupport().addShutdownHook(new ServiceShutdownHook()); + ImageSingletons.add(ServiceSupport.class, new ServiceSupport()); + } +} + +final class ServiceStartupHook implements RuntimeSupport.Hook { + @Override + public void execute(boolean isFirstIsolate) { + if (isFirstIsolate) { + ServiceSupport.singleton().initialize(); + } + } +} + +final class ServiceShutdownHook implements RuntimeSupport.Hook { + @Override + public void execute(boolean isFirstIsolate) { + ServiceSupport.singleton().teardown(); + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceSupport.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceSupport.java new file mode 100644 index 000000000000..08773dc74af2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceSupport.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +import com.oracle.svm.core.heap.GCCause; +import com.oracle.svm.core.Uninterruptible; +import jdk.graal.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.word.UnsignedWord; + +public class ServiceSupport { + private GcNotifier gcNotifier; + private ServiceThread serviceThread; + + @Platforms(Platform.HOSTED_ONLY.class) + public ServiceSupport() { + gcNotifier = new GcNotifier(); + serviceThread = new ServiceThread(gcNotifier); + } + + @Fold + public static ServiceSupport singleton() { + return ImageSingletons.lookup(ServiceSupport.class); + } + + void initialize() { + serviceThread.start(); + } + + /** + * Called from GC code. Allocation Free. + */ + public void beforeCollection(long startTime) { + gcNotifier.beforeCollection(startTime); + } + + /** + * Called from GC code. Allocation Free. + */ + public void afterCollection(boolean isIncremental, GCCause cause, UnsignedWord collectionEpoch, long endTime) { + gcNotifier.afterCollection(isIncremental, cause, collectionEpoch.rawValue(), endTime); + signalServiceThread(); + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + void signalServiceThread() { + serviceThread.signal(); + } + + void teardown() { + serviceThread.shutdown(); + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceThread.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceThread.java new file mode 100644 index 000000000000..f1ed504da363 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceThread.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.jdk.UninterruptibleUtils; +import com.oracle.svm.core.locks.VMSemaphore; +import com.oracle.svm.core.log.Log; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +/** + * This class is the dedicated thread that handles services. For now, the only service is GC + * notifications. + */ +public class ServiceThread extends Thread { + private final UninterruptibleUtils.AtomicBoolean atomicNotify; + private final VMSemaphore semaphore; + private volatile boolean stopped; + private GcNotifier gcNotifier; + + @Platforms(Platform.HOSTED_ONLY.class) + @SuppressWarnings("this-escape") + public ServiceThread(GcNotifier gcNotifier) { + this.gcNotifier = gcNotifier; + this.semaphore = new VMSemaphore("serviceThread"); + this.atomicNotify = new UninterruptibleUtils.AtomicBoolean(false); + setDaemon(true); + } + + /** Awakens to send notifications asynchronously. */ + @Override + public void run() { + while (!stopped) { + if (await()) { + gcNotifier.sendNotification(); + // In the future, we may want to do other things here too. + } + } + } + + private boolean await() { + semaphore.await(); + return atomicNotify.compareAndSet(true, false); + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void signal() { + atomicNotify.set(true); + semaphore.signal(); + } + + public void shutdown() { + this.stopped = true; + this.signal(); + // Wait until the ServiceThread finishes. + try { + this.join(); + } catch (InterruptedException e) { + Log.log().string("Service thread could not shutdown correctly."); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java new file mode 100644 index 000000000000..0555ae8e8b72 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.TargetClass; +import com.sun.management.GcInfo; +import com.sun.management.internal.GcInfoBuilder; + +import java.lang.management.MemoryUsage; + +@TargetClass(value = GcInfo.class, onlyWith = HasGcNotificationSupport.class) +@SuppressWarnings("static-method") +public final class Target_com_sun_management_GcInfo { + @SuppressWarnings("unused") // + @Alias + public Target_com_sun_management_GcInfo(GcInfoBuilder builder, + long index, long startTime, long endTime, + MemoryUsage[] muBeforeGc, + MemoryUsage[] muAfterGc, + Object[] extAttributes) { + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_internal_GcInfoBuilder.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_internal_GcInfoBuilder.java new file mode 100644 index 000000000000..a959ecee6c72 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_internal_GcInfoBuilder.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.util.BasedOnJDKFile; +import com.sun.management.internal.GcInfoBuilder; + +import java.lang.management.GarbageCollectorMXBean; + +@TargetClass(value = GcInfoBuilder.class, onlyWith = HasGcNotificationSupport.class) +@SuppressWarnings("static-method") +public final class Target_com_sun_management_internal_GcInfoBuilder { + @SuppressWarnings("unused") // + @Alias + public Target_com_sun_management_internal_GcInfoBuilder(GarbageCollectorMXBean gc, String[] poolNames) { + } + + /** All GCs have 1 attribute (number of GC threads). */ + @Substitute + private int getNumGcExtAttributes(@SuppressWarnings("unused") GarbageCollectorMXBean gc) { + return 1; + } + + /** This is hardcoded in Hotspot. All GCs have 1 attribute (number of GC threads). */ + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/hotspot/share/services/management.cpp#L1803-L1817") // + @Substitute + private void fillGcAttributeInfo(@SuppressWarnings("unused") GarbageCollectorMXBean gc, + int numAttributes, + String[] attributeNames, + char[] types, + String[] descriptions) { + assert numAttributes == 1; + attributeNames[0] = "GcThreadCount"; + types[0] = 'I'; + descriptions[0] = "Number of GC threads"; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 1ca7cbc4f6f1..d92b542da2c7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -1084,7 +1084,8 @@ protected void onValueUpdate(EconomicMap, Object> values, Integer o @Fold public static final boolean needsExitHandlers() { - return ConcealedOptions.InstallExitHandlers.getValue() || VMInspectionOptions.hasJfrSupport() || VMInspectionOptions.hasNativeMemoryTrackingSupport(); + return ConcealedOptions.InstallExitHandlers.getValue() || VMInspectionOptions.hasJfrSupport() || VMInspectionOptions.hasNativeMemoryTrackingSupport() || + VMInspectionOptions.hasGcNotificationSupport(); } @Option(help = "Overwrites the available number of processors provided by the OS. Any value <= 0 means using the processor count from the OS.")// diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java index 794755a206c3..e3920193d295 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java @@ -60,11 +60,13 @@ public final class VMInspectionOptions { private static final String MONITORING_JMXSERVER_NAME = "jmxserver"; private static final String MONITORING_THREADDUMP_NAME = "threaddump"; private static final String MONITORING_NMT_NAME = "nmt"; + private static final String MONITORING_GC_NOTIFICATIONS = "gcnotif"; private static final List MONITORING_ALL_VALUES = List.of(MONITORING_HEAPDUMP_NAME, MONITORING_JFR_NAME, MONITORING_JVMSTAT_NAME, MONITORING_JMXCLIENT_NAME, MONITORING_JMXSERVER_NAME, - MONITORING_THREADDUMP_NAME, MONITORING_NMT_NAME, MONITORING_ALL_NAME, MONITORING_DEFAULT_NAME); + MONITORING_THREADDUMP_NAME, MONITORING_NMT_NAME, MONITORING_GC_NOTIFICATIONS, MONITORING_ALL_NAME, MONITORING_DEFAULT_NAME); private static final String MONITORING_ALLOWED_VALUES_TEXT = "'" + MONITORING_HEAPDUMP_NAME + "', '" + MONITORING_JFR_NAME + "', '" + MONITORING_JVMSTAT_NAME + "', '" + MONITORING_JMXSERVER_NAME + - "' (experimental), '" + MONITORING_JMXCLIENT_NAME + "' (experimental), '" + MONITORING_THREADDUMP_NAME + "', '" + MONITORING_NMT_NAME + "' (experimental), or '" + + "' (experimental), '" + MONITORING_JMXCLIENT_NAME + "' (experimental), '" + MONITORING_THREADDUMP_NAME + "', '" + MONITORING_GC_NOTIFICATIONS + "', '" + MONITORING_NMT_NAME + + "' (experimental), or '" + MONITORING_ALL_NAME + "' (deprecated behavior: defaults to '" + MONITORING_ALL_NAME + "' if no argument is provided)"; static { @@ -178,6 +180,11 @@ public static boolean hasNativeMemoryTrackingSupport() { return hasAllOrKeywordMonitoringSupport(MONITORING_NMT_NAME); } + @Fold + public static boolean hasGcNotificationSupport() { + return hasAllOrKeywordMonitoringSupport(MONITORING_GC_NOTIFICATIONS); + } + static class DeprecatedOptions { @Option(help = "Enables features that allow the VM to be inspected during run time.", type = OptionType.User, // deprecated = true, deprecationMessage = "Please use '--" + ENABLE_MONITORING_OPTION + "'") // diff --git a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties index e1470477f070..a76ecb0e5a94 100644 --- a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties +++ b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/native-image.properties @@ -8,5 +8,5 @@ Args= \ --features=com.oracle.svm.test.jfr.JfrTestFeature \ --add-opens=java.base/java.lang=ALL-UNNAMED \ --add-exports=org.graalvm.nativeimage.base/com.oracle.svm.util=ALL-UNNAMED \ - --enable-monitoring=jvmstat,jfr,jmxserver,jmxclient,nmt \ + --enable-monitoring=jvmstat,jfr,jmxserver,jmxclient,nmt,gcnotif \ -J--enable-preview \ No newline at end of file diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java new file mode 100644 index 000000000000..107f345f1d8d --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.test.service; + +import com.oracle.svm.core.genscavenge.AbstractGarbageCollectorMXBean; +import com.sun.management.GarbageCollectionNotificationInfo; +import com.sun.management.GcInfo; + +import org.junit.Test; + +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.openmbean.CompositeData; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; + +import static org.junit.Assert.assertTrue; + +public class GcNotificationTests { + + @Test + public void testListenerRegistration() { + int count = 0; + // Get the collector beans and register listeners. + for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) { + if (!(gcBean instanceof NotificationEmitter notificationEmitter)) { + continue; + } + count++; + notificationEmitter.addNotificationListener( + (notification, handback) -> { + }, + notification -> notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION), + null); + // Check listeners were registered correctly + assertTrue(((AbstractGarbageCollectorMXBean) gcBean).hasListeners()); + } + assertTrue("There should be some GCs that are notification emitters", count > 0); + } + + @Test + public void testNotificationData() { + NotificationTestListener ntl = new NotificationTestListener(); + + // Register notification listeners + for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) { + if (!(gcBean instanceof NotificationEmitter notificationEmitter)) { + continue; + } + notificationEmitter.addNotificationListener( + ntl, + notification -> notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION), + null); + } + + // Prompt a GC and check for notifications. + synchronized (ntl) { + System.gc(); + try { + // Wait a reasonable maximum length of time. + ntl.wait(30 * 1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + assertTrue(ntl.success); + } + } + + class NotificationTestListener implements javax.management.NotificationListener { + public boolean success; + + @Override + public void handleNotification(Notification notification, Object handback) { + CompositeData cd = (CompositeData) notification.getUserData(); + GarbageCollectionNotificationInfo notificationInfo = GarbageCollectionNotificationInfo.from(cd); + + assertTrue(notificationInfo.getGcName() != null); + + assertTrue(notificationInfo.getGcAction().equals("end of minor GC") || notificationInfo.getGcAction().equals("end of major GC")); + GcInfo gcInfo = notificationInfo.getGcInfo(); + assertTrue(gcInfo != null); + assertTrue(gcInfo.getDuration() >= 0); // Precision is 1 ms. + long before = gcInfo.getMemoryUsageBeforeGc().get("eden space").getUsed(); + long after = gcInfo.getMemoryUsageAfterGc().get("eden space").getUsed(); + assertTrue(before > 0); + assertTrue(before >= after); + + if (notificationInfo.getGcCause().equals("java.lang.System.gc()")) { + signalFinished(); + } + } + + private synchronized void signalFinished() { + success = true; + notify(); + } + } +} From 109d8b9b3de3e44563c9bb16ae58946787cf7b7e Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 4 Oct 2024 10:36:30 -0400 Subject: [PATCH 02/12] don't emit notifications if there are no requests left. Teardown bug. --- .../com/oracle/svm/core/genscavenge/service/GcNotifier.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java index b5cbd0e6f681..f4de3e0868a6 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java @@ -126,7 +126,10 @@ public void sendNotification() { * We don't need to drain all requests since signals queue at the semaphore level. The rest * of the requests will be handled in subsequent calls to this method by the sevice thread. */ - updateCurrentRequest(); + if (!updateCurrentRequest()) { + // No new requests. + return; + } /* * Iterate to figure out which gc bean to send notifications too. Match with name in From b54ae565de0b41a081ee84500df9d4c80e1a4a5b Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 11 Oct 2024 12:22:21 -0400 Subject: [PATCH 03/12] fix minor feedback. --- .../AbstractGarbageCollectorMXBean.java | 22 +++++---- .../oracle/svm/core/genscavenge/GCImpl.java | 12 ++--- .../svm/core/genscavenge/HeapAccounting.java | 2 +- .../service/GcNotificationFeature.java | 45 +++++++++++++++++++ .../service/GcNotificationRequest.java | 22 +-------- .../core/genscavenge/service/GcNotifier.java | 24 +++++++--- .../service/HasGcNotificationSupport.java | 6 +++ ...iceThread.java => NotificationThread.java} | 12 ++--- ...re.java => NotificationThreadFeature.java} | 21 ++++----- ...rt.java => NotificationThreadSupport.java} | 41 +++++------------ .../Target_com_sun_management_GcInfo.java | 3 +- 11 files changed, 114 insertions(+), 96 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationFeature.java rename substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/{ServiceThread.java => NotificationThread.java} (91%) rename substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/{ServiceFeature.java => NotificationThreadFeature.java} (72%) rename substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/{ServiceSupport.java => NotificationThreadSupport.java} (58%) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java index 228043840189..3100b6c1ce29 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java @@ -36,7 +36,6 @@ import com.sun.management.GarbageCollectionNotificationInfo; import com.sun.management.GcInfo; import com.sun.management.internal.GarbageCollectionNotifInfoCompositeData; -import com.sun.management.internal.GcInfoBuilder; import sun.management.NotificationEmitterSupport; import javax.management.MBeanNotificationInfo; @@ -54,18 +53,10 @@ public abstract class AbstractGarbageCollectorMXBean extends NotificationEmitter @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/hotspot/share/gc/serial/serialHeap.cpp#L718") // private static final String ACTION_MAJOR = "end of major GC"; - private GcInfoBuilder gcInfoBuilder; + private Target_com_sun_management_internal_GcInfoBuilder gcInfoBuilder; private volatile GcInfo gcInfo; private long seqNumber = 0; - private synchronized GcInfoBuilder getGcInfoBuilder() { - if (gcInfoBuilder == null) { - Target_com_sun_management_internal_GcInfoBuilder gib = new Target_com_sun_management_internal_GcInfoBuilder(this, getMemoryPoolNames()); - gcInfoBuilder = SubstrateUtil.cast(gib, GcInfoBuilder.class); - } - return gcInfoBuilder; - } - /** * Use the data taken from the request queue to populate MemoryUsage. The service thread calls * this method. @@ -89,8 +80,8 @@ public void createNotification(GcNotificationRequest request) { } } - // Number of GC threads. - Object[] extAttribute = new Object[]{Integer.valueOf(1)}; + Object[] extAttribute = new Object[1]; + extAttribute[0] = 1; // Number of GC threads. Target_com_sun_management_GcInfo targetGcInfo = new Target_com_sun_management_GcInfo(getGcInfoBuilder(), request.epoch, request.startTime, request.endTime, before, after, extAttribute); gcInfo = SubstrateUtil.cast(targetGcInfo, GcInfo.class); @@ -113,6 +104,13 @@ public void createNotification(GcNotificationRequest request) { sendNotification(notif); } + private Target_com_sun_management_internal_GcInfoBuilder getGcInfoBuilder() { + if (gcInfoBuilder == null) { + gcInfoBuilder = new Target_com_sun_management_internal_GcInfoBuilder(this, getMemoryPoolNames()); + } + return gcInfoBuilder; + } + private long getNextSeqNumber() { return ++seqNumber; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java index 3392de746964..ea53572d0bb5 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java @@ -66,7 +66,8 @@ import com.oracle.svm.core.genscavenge.HeapChunk.Header; import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; import com.oracle.svm.core.genscavenge.remset.RememberedSet; -import com.oracle.svm.core.genscavenge.service.ServiceSupport; +import com.oracle.svm.core.genscavenge.service.GcNotifier; +import com.oracle.svm.core.genscavenge.service.HasGcNotificationSupport; import com.oracle.svm.core.graal.RuntimeCompilation; import com.oracle.svm.core.heap.CodeReferenceMapDecoder; import com.oracle.svm.core.heap.GC; @@ -104,7 +105,6 @@ import com.oracle.svm.core.threadlocal.VMThreadLocalSupport; import com.oracle.svm.core.util.TimeUtils; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.core.VMInspectionOptions; import jdk.graal.compiler.api.replacements.Fold; @@ -242,8 +242,8 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) || try { ThreadLocalAllocation.disableAndFlushForAllThreads(); GenScavengeMemoryPoolMXBeans.singleton().notifyBeforeCollection(); - if (VMInspectionOptions.hasGcNotificationSupport()) { - ServiceSupport.singleton().beforeCollection(Isolates.getCurrentUptimeMillis()); + if (HasGcNotificationSupport.get()) { + GcNotifier.singleton().beforeCollection(TimeUtils.roundNanosToMillis(collectionTimer.getOpenedTime() - Isolates.getCurrentStartNanoTime())); } HeapImpl.getAccounting().notifyBeforeCollection(); @@ -262,8 +262,8 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) || GenScavengeMemoryPoolMXBeans.singleton().notifyAfterCollection(); ChunkBasedCommittedMemoryProvider.get().afterGarbageCollection(); - if (VMInspectionOptions.hasGcNotificationSupport()) { - ServiceSupport.singleton().afterCollection(!isCompleteCollection(), cause, getCollectionEpoch(), Isolates.getCurrentUptimeMillis()); + if (HasGcNotificationSupport.get()) { + GcNotifier.singleton().afterCollection(!completeCollection, cause, getCollectionEpoch().rawValue(), TimeUtils.roundNanosToMillis(collectionTimer.getClosedTime() - Isolates.getCurrentStartNanoTime())); } printGCAfter(cause); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapAccounting.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapAccounting.java index 523e28f6cc2c..e1b3153abcce 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapAccounting.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapAccounting.java @@ -79,7 +79,7 @@ public void notifyAfterCollection() { invalidData = false; } - public HeapSizes getHeapSizesBeforeGc() { + HeapSizes getHeapSizesBeforeGc() { return beforeGc; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationFeature.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationFeature.java new file mode 100644 index 000000000000..c1188a414644 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationFeature.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.genscavenge.service; + +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import org.graalvm.nativeimage.ImageSingletons; + +@AutomaticallyRegisteredFeature +public class GcNotificationFeature implements InternalFeature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return HasGcNotificationSupport.get(); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + ImageSingletons.add(GcNotifier.class, new GcNotifier()); + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java index 64abd9ec5414..f316156fe0da 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java @@ -36,8 +36,8 @@ * a point in the future. */ public class GcNotificationRequest { - private PoolMemoryUsage[] before; - private PoolMemoryUsage[] after; + private final PoolMemoryUsage[] before; + private final PoolMemoryUsage[] after; // Times since the VM started. public long startTime; @@ -83,22 +83,4 @@ public PoolMemoryUsage getPoolAfter(int i) { return after[i]; } - @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - void copyTo(GcNotificationRequest other) { - other.startTime = startTime; - other.epoch = epoch; - other.endTime = endTime; - other.cause = cause; - other.timestamp = timestamp; - other.isIncremental = isIncremental; - - for (int index = 0; index < before.length; index++) { - other.setPoolBefore(index, before[index].used, before[index].committed, before[index].name); - } - - for (int index = 0; index < after.length; index++) { - other.setPoolAfter(index, after[index].used, after[index].committed, after[index].name); - } - } - } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java index f4de3e0868a6..7e04445574f9 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java @@ -26,9 +26,11 @@ package com.oracle.svm.core.genscavenge.service; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.Platform; import com.oracle.svm.core.Uninterruptible; +import jdk.graal.compiler.api.replacements.Fold; import com.oracle.svm.core.genscavenge.AbstractMemoryPoolMXBean; import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans; @@ -52,8 +54,8 @@ public class GcNotifier { // 5 is probably more than enough, although making the queue larger isn't a problem either. private static final int QUEUE_SIZE = 5; - private static volatile int rear; - private static volatile int front; + private static int rear; + private static int front; GcNotificationRequest[] requests; GcNotificationRequest currentRequest; @@ -70,8 +72,13 @@ public class GcNotifier { front = 0; } + @Fold + public static GcNotifier singleton() { + return ImageSingletons.lookup(GcNotifier.class); + } + /** Called during GC. */ - void beforeCollection(long startTime) { + public void beforeCollection(long startTime) { assert VMOperation.isInProgressAtSafepoint(); AbstractMemoryPoolMXBean[] beans = GenScavengeMemoryPoolMXBeans.singleton().getMXBeans(); @@ -86,7 +93,7 @@ void beforeCollection(long startTime) { } /** Called during GC. */ - void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime) { + public void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime) { assert VMOperation.isInProgressAtSafepoint(); AbstractMemoryPoolMXBean[] beans = GenScavengeMemoryPoolMXBeans.singleton().getMXBeans(); @@ -108,6 +115,7 @@ void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endT if (front == rear) { front = incremented(front); } + org.graalvm.nativeimage.ImageSingletons.lookup(NotificationThreadSupport.class).signalServiceThread(); } @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @@ -136,8 +144,9 @@ public void sendNotification() { * NotificationRequest class that was dequeued. */ GarbageCollectorMXBean gcBean = null; + String targetBeanName = currentRequest.isIncremental ? YOUNG_GEN_SCAVENGER : COMPLETE_SCAVENGER; for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { - if ((currentRequest.isIncremental && bean.getName().equals(YOUNG_GEN_SCAVENGER)) || (!currentRequest.isIncremental && bean.getName().equals(COMPLETE_SCAVENGER))) { + if (bean.getName().equals(targetBeanName)) { gcBean = bean; break; } @@ -155,7 +164,10 @@ private boolean updateCurrentRequest() { return false; } // Copy the data to avoid data being overwritten by new writes to the queue. - requests[front].copyTo(currentRequest); + GcNotificationRequest temp = currentRequest; + currentRequest = requests[front]; + requests[front] = temp; + front = incremented(front); return true; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java index 95fa6d9f8235..3d1e84a4d41b 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java @@ -29,10 +29,16 @@ import java.util.function.BooleanSupplier; import com.oracle.svm.core.VMInspectionOptions; +import jdk.graal.compiler.api.replacements.Fold; public class HasGcNotificationSupport implements BooleanSupplier { @Override public boolean getAsBoolean() { + return get(); + } + + @Fold + public static boolean get() { return VMInspectionOptions.hasGcNotificationSupport(); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceThread.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThread.java similarity index 91% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceThread.java rename to substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThread.java index f1ed504da363..a0a954ad0bcf 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceThread.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThread.java @@ -37,16 +37,14 @@ * This class is the dedicated thread that handles services. For now, the only service is GC * notifications. */ -public class ServiceThread extends Thread { +public class NotificationThread extends Thread { private final UninterruptibleUtils.AtomicBoolean atomicNotify; private final VMSemaphore semaphore; private volatile boolean stopped; - private GcNotifier gcNotifier; @Platforms(Platform.HOSTED_ONLY.class) @SuppressWarnings("this-escape") - public ServiceThread(GcNotifier gcNotifier) { - this.gcNotifier = gcNotifier; + public NotificationThread() { this.semaphore = new VMSemaphore("serviceThread"); this.atomicNotify = new UninterruptibleUtils.AtomicBoolean(false); setDaemon(true); @@ -57,7 +55,9 @@ public ServiceThread(GcNotifier gcNotifier) { public void run() { while (!stopped) { if (await()) { - gcNotifier.sendNotification(); + if (HasGcNotificationSupport.get()) { + GcNotifier.singleton().sendNotification(); + } // In the future, we may want to do other things here too. } } @@ -77,7 +77,7 @@ public void signal() { public void shutdown() { this.stopped = true; this.signal(); - // Wait until the ServiceThread finishes. + // Wait until the NotificationThread finishes. try { this.join(); } catch (InterruptedException e) { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceFeature.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadFeature.java similarity index 72% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceFeature.java rename to substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadFeature.java index 82f54a2147d2..9c3cbfe7d436 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceFeature.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadFeature.java @@ -29,37 +29,34 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.jdk.RuntimeSupport; -import com.oracle.svm.core.VMInspectionOptions; import org.graalvm.nativeimage.ImageSingletons; @AutomaticallyRegisteredFeature -public class ServiceFeature implements InternalFeature { +public class NotificationThreadFeature implements InternalFeature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - return VMInspectionOptions.hasGcNotificationSupport(); + return HasGcNotificationSupport.get(); // Can extend this list later. } @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - RuntimeSupport.getRuntimeSupport().addStartupHook(new ServiceStartupHook()); - RuntimeSupport.getRuntimeSupport().addShutdownHook(new ServiceShutdownHook()); - ImageSingletons.add(ServiceSupport.class, new ServiceSupport()); + ImageSingletons.add(NotificationThreadSupport.class, new NotificationThreadSupport()); + RuntimeSupport.getRuntimeSupport().addStartupHook(new NotificationThreadStartupHook()); + RuntimeSupport.getRuntimeSupport().addShutdownHook(new NotificationThreadShutdownHook()); } } -final class ServiceStartupHook implements RuntimeSupport.Hook { +final class NotificationThreadStartupHook implements RuntimeSupport.Hook { @Override public void execute(boolean isFirstIsolate) { - if (isFirstIsolate) { - ServiceSupport.singleton().initialize(); - } + NotificationThreadSupport.singleton().initialize(); } } -final class ServiceShutdownHook implements RuntimeSupport.Hook { +final class NotificationThreadShutdownHook implements RuntimeSupport.Hook { @Override public void execute(boolean isFirstIsolate) { - ServiceSupport.singleton().teardown(); + NotificationThreadSupport.singleton().teardown(); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceSupport.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadSupport.java similarity index 58% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceSupport.java rename to substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadSupport.java index 08773dc74af2..f74c52228ed9 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/ServiceSupport.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadSupport.java @@ -26,54 +26,33 @@ package com.oracle.svm.core.genscavenge.service; -import com.oracle.svm.core.heap.GCCause; -import com.oracle.svm.core.Uninterruptible; import jdk.graal.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.word.UnsignedWord; -public class ServiceSupport { - private GcNotifier gcNotifier; - private ServiceThread serviceThread; +public class NotificationThreadSupport { + private final NotificationThread notificationThread; @Platforms(Platform.HOSTED_ONLY.class) - public ServiceSupport() { - gcNotifier = new GcNotifier(); - serviceThread = new ServiceThread(gcNotifier); + public NotificationThreadSupport() { + notificationThread = new NotificationThread(); } @Fold - public static ServiceSupport singleton() { - return ImageSingletons.lookup(ServiceSupport.class); + public static NotificationThreadSupport singleton() { + return ImageSingletons.lookup(NotificationThreadSupport.class); } void initialize() { - serviceThread.start(); + notificationThread.start(); } - /** - * Called from GC code. Allocation Free. - */ - public void beforeCollection(long startTime) { - gcNotifier.beforeCollection(startTime); - } - - /** - * Called from GC code. Allocation Free. - */ - public void afterCollection(boolean isIncremental, GCCause cause, UnsignedWord collectionEpoch, long endTime) { - gcNotifier.afterCollection(isIncremental, cause, collectionEpoch.rawValue(), endTime); - signalServiceThread(); - } - - @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - void signalServiceThread() { - serviceThread.signal(); + public void signalServiceThread() { + notificationThread.signal(); } void teardown() { - serviceThread.shutdown(); + notificationThread.shutdown(); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java index 0555ae8e8b72..7c6903261131 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java @@ -29,7 +29,6 @@ import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.TargetClass; import com.sun.management.GcInfo; -import com.sun.management.internal.GcInfoBuilder; import java.lang.management.MemoryUsage; @@ -38,7 +37,7 @@ public final class Target_com_sun_management_GcInfo { @SuppressWarnings("unused") // @Alias - public Target_com_sun_management_GcInfo(GcInfoBuilder builder, + public Target_com_sun_management_GcInfo(Target_com_sun_management_internal_GcInfoBuilder builder, long index, long startTime, long endTime, MemoryUsage[] muBeforeGc, MemoryUsage[] muAfterGc, From d56a7582d94c301ff463d19282266706abe6e368 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Tue, 15 Oct 2024 11:24:31 -0400 Subject: [PATCH 04/12] split into GC independent/dependent code --- .../CompleteGarbageCollectorMXBean.java | 1 + .../oracle/svm/core/genscavenge/GCImpl.java | 12 ++--- .../GenScavengeMemoryPoolMXBeans.java | 45 ++++++++++--------- .../IncrementalGarbageCollectorMXBean.java | 1 + .../graal/GenScavengeGCFeature.java | 3 +- .../gc}/AbstractGarbageCollectorMXBean.java | 16 +++---- .../core/gc}/AbstractMemoryPoolMXBean.java | 14 +++--- .../core/gc/MemoryPoolMXBeansProvider.java | 45 +++++++++++++++++++ .../notification}/GcNotificationFeature.java | 2 +- .../notification}/GcNotificationRequest.java | 2 +- .../svm/core/notification}/GcNotifier.java | 37 +++++++-------- .../HasGcNotificationSupport.java | 2 +- .../notification}/NotificationThread.java | 4 +- .../NotificationThreadFeature.java | 8 ++-- .../NotificationThreadSupport.java | 2 +- .../core/notification}/PoolMemoryUsage.java | 2 +- .../Target_com_sun_management_GcInfo.java | 2 +- ...sun_management_internal_GcInfoBuilder.java | 2 +- .../svm/test/service/GcNotificationTests.java | 2 +- 19 files changed, 126 insertions(+), 76 deletions(-) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge => com.oracle.svm.core/src/com/oracle/svm/core/gc}/AbstractGarbageCollectorMXBean.java (91%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge => com.oracle.svm.core/src/com/oracle/svm/core/gc}/AbstractMemoryPoolMXBean.java (93%) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/GcNotificationFeature.java (97%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/GcNotificationRequest.java (98%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/GcNotifier.java (80%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/HasGcNotificationSupport.java (97%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/NotificationThread.java (97%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/NotificationThreadFeature.java (86%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/NotificationThreadSupport.java (97%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/PoolMemoryUsage.java (96%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/Target_com_sun_management_GcInfo.java (97%) rename substratevm/src/{com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service => com.oracle.svm.core/src/com/oracle/svm/core/notification}/Target_com_sun_management_internal_GcInfoBuilder.java (98%) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java index 0355a26ed94d..2ca6b4a97664 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java @@ -28,6 +28,7 @@ import javax.management.ObjectName; +import com.oracle.svm.core.gc.AbstractGarbageCollectorMXBean; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java index ea53572d0bb5..c56787c09995 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java @@ -60,14 +60,15 @@ import com.oracle.svm.core.deopt.DeoptimizationSlotPacking; import com.oracle.svm.core.deopt.DeoptimizedFrame; import com.oracle.svm.core.deopt.Deoptimizer; +import com.oracle.svm.core.gc.MemoryPoolMXBeansProvider; import com.oracle.svm.core.genscavenge.AlignedHeapChunk.AlignedHeader; import com.oracle.svm.core.genscavenge.BasicCollectionPolicies.NeverCollect; import com.oracle.svm.core.genscavenge.HeapAccounting.HeapSizes; import com.oracle.svm.core.genscavenge.HeapChunk.Header; import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; import com.oracle.svm.core.genscavenge.remset.RememberedSet; -import com.oracle.svm.core.genscavenge.service.GcNotifier; -import com.oracle.svm.core.genscavenge.service.HasGcNotificationSupport; +import com.oracle.svm.core.notification.GcNotifier; +import com.oracle.svm.core.notification.HasGcNotificationSupport; import com.oracle.svm.core.graal.RuntimeCompilation; import com.oracle.svm.core.heap.CodeReferenceMapDecoder; import com.oracle.svm.core.heap.GC; @@ -241,7 +242,7 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) || Timer collectionTimer = timers.collection.open(); try { ThreadLocalAllocation.disableAndFlushForAllThreads(); - GenScavengeMemoryPoolMXBeans.singleton().notifyBeforeCollection(); + MemoryPoolMXBeansProvider.get().notifyBeforeCollection(); if (HasGcNotificationSupport.get()) { GcNotifier.singleton().beforeCollection(TimeUtils.roundNanosToMillis(collectionTimer.getOpenedTime() - Isolates.getCurrentStartNanoTime())); } @@ -259,11 +260,12 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) || accounting.updateCollectionCountAndTime(completeCollection, collectionTimer.getMeasuredNanos()); HeapImpl.getAccounting().notifyAfterCollection(); - GenScavengeMemoryPoolMXBeans.singleton().notifyAfterCollection(); + MemoryPoolMXBeansProvider.get().notifyAfterCollection(); ChunkBasedCommittedMemoryProvider.get().afterGarbageCollection(); if (HasGcNotificationSupport.get()) { - GcNotifier.singleton().afterCollection(!completeCollection, cause, getCollectionEpoch().rawValue(), TimeUtils.roundNanosToMillis(collectionTimer.getClosedTime() - Isolates.getCurrentStartNanoTime())); + GcNotifier.singleton().afterCollection(!completeCollection, cause, getCollectionEpoch().rawValue(), + TimeUtils.roundNanosToMillis(collectionTimer.getClosedTime() - Isolates.getCurrentStartNanoTime())); } printGCAfter(cause); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java index 65a83734704f..1391c21eb81e 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java @@ -27,7 +27,9 @@ import java.lang.management.MemoryUsage; -import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.gc.AbstractMemoryPoolMXBean; +import com.oracle.svm.core.gc.MemoryPoolMXBeansProvider; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.UnsignedWord; @@ -36,9 +38,7 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.util.VMError; -import jdk.graal.compiler.api.replacements.Fold; - -public class GenScavengeMemoryPoolMXBeans { +public class GenScavengeMemoryPoolMXBeans implements MemoryPoolMXBeansProvider { public static final String YOUNG_GEN_SCAVENGER = "young generation scavenger"; public static final String COMPLETE_SCAVENGER = "complete scavenger"; static final String EPSILON_SCAVENGER = "epsilon scavenger"; @@ -66,21 +66,24 @@ public GenScavengeMemoryPoolMXBeans() { } } - @Fold - public static GenScavengeMemoryPoolMXBeans singleton() { - return ImageSingletons.lookup(GenScavengeMemoryPoolMXBeans.class); - } - + @Override public AbstractMemoryPoolMXBean[] getMXBeans() { return mxBeans; } + @Override + public String getCollectorName(boolean isIncremental) { + return isIncremental ? YOUNG_GEN_SCAVENGER : COMPLETE_SCAVENGER; + } + + @Override public void notifyBeforeCollection() { for (AbstractMemoryPoolMXBean mxBean : mxBeans) { mxBean.beforeCollection(); } } + @Override public void notifyAfterCollection() { for (AbstractMemoryPoolMXBean mxBean : mxBeans) { mxBean.afterCollection(); @@ -95,17 +98,17 @@ static final class EdenMemoryPoolMXBean extends AbstractMemoryPoolMXBean { } @Override - void beforeCollection() { + public void beforeCollection() { updatePeakUsage(HeapImpl.getAccounting().getEdenUsedBytes()); } @Override - void afterCollection() { + public void afterCollection() { /* Nothing to do. */ } @Override - UnsignedWord computeInitialValue() { + protected UnsignedWord computeInitialValue() { return GCImpl.getPolicy().getInitialEdenSize(); } @@ -139,17 +142,17 @@ static final class SurvivorMemoryPoolMXBean extends AbstractMemoryPoolMXBean { } @Override - void beforeCollection() { + public void beforeCollection() { /* Nothing to do. */ } @Override - void afterCollection() { + public void afterCollection() { updatePeakUsage(HeapImpl.getAccounting().getSurvivorUsedBytes()); } @Override - UnsignedWord computeInitialValue() { + protected UnsignedWord computeInitialValue() { return GCImpl.getPolicy().getInitialSurvivorSize(); } @@ -182,17 +185,17 @@ static final class OldGenerationMemoryPoolMXBean extends AbstractMemoryPoolMXBea } @Override - void beforeCollection() { + public void beforeCollection() { /* Nothing to do. */ } @Override - void afterCollection() { + public void afterCollection() { updatePeakUsage(HeapImpl.getAccounting().getOldUsedBytes()); } @Override - UnsignedWord computeInitialValue() { + protected UnsignedWord computeInitialValue() { return GCImpl.getPolicy().getInitialOldSize(); } @@ -225,17 +228,17 @@ static final class EpsilonMemoryPoolMXBean extends AbstractMemoryPoolMXBean { } @Override - void beforeCollection() { + public void beforeCollection() { throw VMError.shouldNotReachHereAtRuntime(); // ExcludeFromJacocoGeneratedReport } @Override - void afterCollection() { + public void afterCollection() { throw VMError.shouldNotReachHereAtRuntime(); // ExcludeFromJacocoGeneratedReport } @Override - UnsignedWord computeInitialValue() { + protected UnsignedWord computeInitialValue() { return GCImpl.getPolicy().getMinimumHeapSize(); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java index e5800892f84e..af573672af8d 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java @@ -31,6 +31,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.core.gc.AbstractGarbageCollectorMXBean; import com.oracle.svm.core.util.TimeUtils; import sun.management.Util; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java index fe67125edde0..c2e2cb04ce54 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java @@ -36,6 +36,7 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.gc.MemoryPoolMXBeansProvider; import com.oracle.svm.core.genscavenge.ChunkedImageHeapLayouter; import com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans; import com.oracle.svm.core.genscavenge.HeapImpl; @@ -84,7 +85,7 @@ public void afterRegistration(AfterRegistrationAccess access) { ImageSingletons.add(BarrierSetProvider.class, rememberedSet); GenScavengeMemoryPoolMXBeans memoryPoolMXBeans = new GenScavengeMemoryPoolMXBeans(); - ImageSingletons.add(GenScavengeMemoryPoolMXBeans.class, memoryPoolMXBeans); + ImageSingletons.add(MemoryPoolMXBeansProvider.class, memoryPoolMXBeans); ImageSingletons.add(GCRelatedMXBeans.class, new GenScavengeRelatedMXBeans(memoryPoolMXBeans)); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java similarity index 91% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java index 3100b6c1ce29..6c7713c57427 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java @@ -23,15 +23,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.genscavenge; +package com.oracle.svm.core.gc; import com.oracle.svm.core.util.BasedOnJDKFile; import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.genscavenge.service.GcNotificationRequest; -import com.oracle.svm.core.genscavenge.service.PoolMemoryUsage; -import com.oracle.svm.core.genscavenge.service.Target_com_sun_management_GcInfo; -import com.oracle.svm.core.genscavenge.service.Target_com_sun_management_internal_GcInfoBuilder; +import com.oracle.svm.core.notification.GcNotificationRequest; +import com.oracle.svm.core.notification.PoolMemoryUsage; +import com.oracle.svm.core.notification.Target_com_sun_management_GcInfo; +import com.oracle.svm.core.notification.Target_com_sun_management_internal_GcInfoBuilder; import com.sun.management.GarbageCollectionNotificationInfo; import com.sun.management.GcInfo; @@ -58,11 +58,11 @@ public abstract class AbstractGarbageCollectorMXBean extends NotificationEmitter private long seqNumber = 0; /** - * Use the data taken from the request queue to populate MemoryUsage. The service thread calls - * this method. + * Use the data taken from the request queue to populate MemoryUsage. The notification thread + * calls this method. */ public void createNotification(GcNotificationRequest request) { - AbstractMemoryPoolMXBean[] beans = GenScavengeMemoryPoolMXBeans.singleton().getMXBeans(); + AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); String[] poolNames = getMemoryPoolNames(); MemoryUsage[] before = new MemoryUsage[poolNames.length]; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractMemoryPoolMXBean.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractMemoryPoolMXBean.java similarity index 93% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractMemoryPoolMXBean.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractMemoryPoolMXBean.java index 88b4718e5ccd..589a625cc2d7 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractMemoryPoolMXBean.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractMemoryPoolMXBean.java @@ -23,7 +23,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.genscavenge; +package com.oracle.svm.core.gc; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; @@ -72,17 +72,17 @@ public UnsignedWord getCommittedBytes() { return getUsedBytes(); } - abstract UnsignedWord computeInitialValue(); + protected abstract UnsignedWord computeInitialValue(); - abstract void beforeCollection(); + public abstract void beforeCollection(); - abstract void afterCollection(); + public abstract void afterCollection(); - MemoryUsage memoryUsage(UnsignedWord usedAndCommitted) { + protected MemoryUsage memoryUsage(UnsignedWord usedAndCommitted) { return memoryUsage(usedAndCommitted, usedAndCommitted); } - MemoryUsage memoryUsage(UnsignedWord used, UnsignedWord committed) { + protected MemoryUsage memoryUsage(UnsignedWord used, UnsignedWord committed) { return memoryUsage(used.rawValue(), committed.rawValue()); } @@ -170,7 +170,7 @@ public void resetPeakUsage() { peakUsage.set(WordFactory.zero()); } - void updatePeakUsage(UnsignedWord value) { + protected void updatePeakUsage(UnsignedWord value) { UnsignedWord current; do { current = peakUsage.get(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java new file mode 100644 index 000000000000..6e0abf0b11de --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.gc; + +import jdk.graal.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; + +public interface MemoryPoolMXBeansProvider { + @Fold + static MemoryPoolMXBeansProvider get() { + return ImageSingletons.lookup(MemoryPoolMXBeansProvider.class); + } + + AbstractMemoryPoolMXBean[] getMXBeans(); + + String getCollectorName(boolean isIncremental); + + void notifyBeforeCollection(); + + void notifyAfterCollection(); +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationFeature.java similarity index 97% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationFeature.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationFeature.java index c1188a414644..cb5fb2cfc51c 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationFeature.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java similarity index 98% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java index f316156fe0da..7d662c56406f 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotificationRequest.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; import com.oracle.svm.core.heap.GCCause; import org.graalvm.nativeimage.Platform; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java similarity index 80% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java index 7e04445574f9..98d66894e697 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/GcNotifier.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java @@ -24,32 +24,29 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.Platform; import com.oracle.svm.core.Uninterruptible; import jdk.graal.compiler.api.replacements.Fold; -import com.oracle.svm.core.genscavenge.AbstractMemoryPoolMXBean; +import com.oracle.svm.core.gc.AbstractMemoryPoolMXBean; +import com.oracle.svm.core.gc.MemoryPoolMXBeansProvider; import com.oracle.svm.core.heap.GCCause; -import com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans; -import com.oracle.svm.core.genscavenge.AbstractGarbageCollectorMXBean; +import com.oracle.svm.core.gc.AbstractGarbageCollectorMXBean; import com.oracle.svm.core.thread.VMOperation; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; -import static com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans.COMPLETE_SCAVENGER; -import static com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans.OLD_GEN_SPACE; -import static com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans.YOUNG_GEN_SCAVENGER; - /** * This class keeps a circular queue of requests to later use when emitting GC notifications. The * requests are pre-allocated so that they can be used during a GC. * * This class is responsible for enqueuing requests during GC safepoints and later dequeuing - * requests to generate notifications when the service thread handles signals outside of safepoints. + * requests to generate notifications when the notification thread handles signals outside of + * safepoints. */ public class GcNotifier { // 5 is probably more than enough, although making the queue larger isn't a problem either. @@ -65,9 +62,9 @@ public class GcNotifier { // Pre-allocate the queue so we can use it later from allocation free code. requests = new GcNotificationRequest[QUEUE_SIZE]; for (int i = 0; i < QUEUE_SIZE; i++) { - requests[i] = new GcNotificationRequest(GenScavengeMemoryPoolMXBeans.singleton().getMXBeans().length); + requests[i] = new GcNotificationRequest(MemoryPoolMXBeansProvider.get().getMXBeans().length); } - currentRequest = new GcNotificationRequest(GenScavengeMemoryPoolMXBeans.singleton().getMXBeans().length); + currentRequest = new GcNotificationRequest(MemoryPoolMXBeansProvider.get().getMXBeans().length); rear = 0; front = 0; } @@ -81,7 +78,7 @@ public static GcNotifier singleton() { public void beforeCollection(long startTime) { assert VMOperation.isInProgressAtSafepoint(); - AbstractMemoryPoolMXBean[] beans = GenScavengeMemoryPoolMXBeans.singleton().getMXBeans(); + AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); for (int i = 0; i < beans.length; i++) { /* * Always add the old generation pool even if we don't end up using it (since we don't @@ -96,11 +93,11 @@ public void beforeCollection(long startTime) { public void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime) { assert VMOperation.isInProgressAtSafepoint(); - AbstractMemoryPoolMXBean[] beans = GenScavengeMemoryPoolMXBeans.singleton().getMXBeans(); + AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); for (int i = 0; i < beans.length; i++) { - if (isIncremental && beans[i].getName().equals(OLD_GEN_SPACE)) { - continue; - } +// if (isIncremental && beans[i].getName().equals(OLD_GEN_SPACE)) { +// continue; +// } requests[rear].setPoolAfter(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); } requests[rear].endTime = endTime; @@ -124,8 +121,8 @@ private static int incremented(int value) { } /** - * Called by the service thread. Sends a notification if any are queued. If none are queued, - * then return immediately. + * Called by the notification thread. Sends a notification if any are queued. If none are + * queued, then return immediately. */ public void sendNotification() { assert !VMOperation.isInProgressAtSafepoint(); @@ -144,7 +141,7 @@ public void sendNotification() { * NotificationRequest class that was dequeued. */ GarbageCollectorMXBean gcBean = null; - String targetBeanName = currentRequest.isIncremental ? YOUNG_GEN_SCAVENGER : COMPLETE_SCAVENGER; + String targetBeanName = MemoryPoolMXBeansProvider.get().getCollectorName(currentRequest.isIncremental); for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { if (bean.getName().equals(targetBeanName)) { gcBean = bean; @@ -156,7 +153,7 @@ public void sendNotification() { ((AbstractGarbageCollectorMXBean) gcBean).createNotification(currentRequest); } - /** Called by the service thread. */ + /** Called by the notification thread. */ @Uninterruptible(reason = "Avoid pausing for a GC safepoint with could cause races with pushes to the request queue.") private boolean updateCurrentRequest() { // If there's nothing to read, return immediately. diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/HasGcNotificationSupport.java similarity index 97% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/HasGcNotificationSupport.java index 3d1e84a4d41b..c40ecb17acc8 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/HasGcNotificationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/HasGcNotificationSupport.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; import java.util.function.BooleanSupplier; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java similarity index 97% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThread.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java index a0a954ad0bcf..2f03c09e0f89 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.jdk.UninterruptibleUtils; @@ -34,7 +34,7 @@ import org.graalvm.nativeimage.Platforms; /** - * This class is the dedicated thread that handles services. For now, the only service is GC + * This class is the dedicated thread that handles services. For now, the only notification is GC * notifications. */ public class NotificationThread extends Thread { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadFeature.java similarity index 86% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadFeature.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadFeature.java index 9c3cbfe7d436..5463d9ae8182 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadFeature.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; @@ -41,9 +41,9 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - ImageSingletons.add(NotificationThreadSupport.class, new NotificationThreadSupport()); - RuntimeSupport.getRuntimeSupport().addStartupHook(new NotificationThreadStartupHook()); - RuntimeSupport.getRuntimeSupport().addShutdownHook(new NotificationThreadShutdownHook()); + ImageSingletons.add(NotificationThreadSupport.class, new NotificationThreadSupport()); + RuntimeSupport.getRuntimeSupport().addStartupHook(new NotificationThreadStartupHook()); + RuntimeSupport.getRuntimeSupport().addShutdownHook(new NotificationThreadShutdownHook()); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadSupport.java similarity index 97% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadSupport.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadSupport.java index f74c52228ed9..d04af9e04a9f 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/NotificationThreadSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadSupport.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; import jdk.graal.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/PoolMemoryUsage.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java similarity index 96% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/PoolMemoryUsage.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java index 6d630c6213a6..39f487a25ccd 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/PoolMemoryUsage.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; /** Used from allocation free code and later converted to MemoryUsage. */ public class PoolMemoryUsage { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/Target_com_sun_management_GcInfo.java similarity index 97% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/Target_com_sun_management_GcInfo.java index 7c6903261131..14ceea0fed2e 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_GcInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/Target_com_sun_management_GcInfo.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.TargetClass; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_internal_GcInfoBuilder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/Target_com_sun_management_internal_GcInfoBuilder.java similarity index 98% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_internal_GcInfoBuilder.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/Target_com_sun_management_internal_GcInfoBuilder.java index a959ecee6c72..c0016054b9bf 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/service/Target_com_sun_management_internal_GcInfoBuilder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/Target_com_sun_management_internal_GcInfoBuilder.java @@ -24,7 +24,7 @@ * questions. */ -package com.oracle.svm.core.genscavenge.service; +package com.oracle.svm.core.notification; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Substitute; diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java index 107f345f1d8d..d21f8ea67366 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java @@ -26,7 +26,7 @@ package com.oracle.svm.test.service; -import com.oracle.svm.core.genscavenge.AbstractGarbageCollectorMXBean; +import com.oracle.svm.core.gc.AbstractGarbageCollectorMXBean; import com.sun.management.GarbageCollectionNotificationInfo; import com.sun.management.GcInfo; From 299dcfc19dbe5fbe0b22dcfaf9fce223601b346b Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Tue, 15 Oct 2024 15:01:47 -0400 Subject: [PATCH 05/12] refactor queue classes --- .../CompleteGarbageCollectorMXBean.java | 5 ++ .../GenScavengeMemoryPoolMXBeans.java | 5 ++ .../IncrementalGarbageCollectorMXBean.java | 5 ++ .../gc/AbstractGarbageCollectorMXBean.java | 22 +++--- .../notification/GcNotificationRequest.java | 75 ++++++++++++++----- .../svm/core/notification/GcNotifier.java | 31 +------- .../core/notification/NotificationThread.java | 7 ++ .../core/notification/PoolMemoryUsage.java | 24 +++++- 8 files changed, 118 insertions(+), 56 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java index 2ca6b4a97664..e90921727ee4 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java @@ -77,4 +77,9 @@ public boolean isValid() { public ObjectName getObjectName() { return Util.newObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE, getName()); } + + @Override + protected int gcThreadCount() { + return 1; + } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java index 1391c21eb81e..4b592ae195f7 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java @@ -132,6 +132,11 @@ public MemoryUsage getCollectionUsage() { public UnsignedWord getUsedBytes() { return HeapImpl.getAccounting().getEdenUsedBytes(); } + + @Override + public UnsignedWord getCommittedBytes() { + return HeapImpl.getAccounting().getEdenUsedBytes().add(HeapImpl.getAccounting().getBytesInUnusedChunks()); + } } static final class SurvivorMemoryPoolMXBean extends AbstractMemoryPoolMXBean { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java index af573672af8d..6309f4882265 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java @@ -76,4 +76,9 @@ public boolean isValid() { public ObjectName getObjectName() { return Util.newObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE, getName()); } + + @Override + protected int gcThreadCount() { + return 1; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java index 6c7713c57427..7b80ddf45e41 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java @@ -34,6 +34,7 @@ import com.oracle.svm.core.notification.Target_com_sun_management_internal_GcInfoBuilder; import com.sun.management.GarbageCollectionNotificationInfo; +import com.sun.management.GarbageCollectorMXBean; import com.sun.management.GcInfo; import com.sun.management.internal.GarbageCollectionNotifInfoCompositeData; import sun.management.NotificationEmitterSupport; @@ -45,7 +46,7 @@ import java.lang.management.MemoryUsage; public abstract class AbstractGarbageCollectorMXBean extends NotificationEmitterSupport - implements com.sun.management.GarbageCollectorMXBean, NotificationEmitter { + implements GarbageCollectorMXBean, NotificationEmitter { @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/hotspot/share/gc/serial/serialHeap.cpp#L451") // private static final String ACTION_MINOR = "end of minor GC"; @@ -72,24 +73,25 @@ public void createNotification(GcNotificationRequest request) { for (int i = 0; i < poolNames.length; i++) { for (int j = 0; j < beans.length; j++) { PoolMemoryUsage pmu = request.getPoolBefore(j); - if (pmu.name != null && pmu.name.equals(poolNames[i])) { - before[i] = beans[j].memoryUsage(pmu.used, pmu.committed); + if (pmu.getName() != null && pmu.getName().equals(poolNames[i])) { + before[i] = beans[j].memoryUsage(pmu.getUsed(), pmu.getCommitted()); pmu = request.getPoolAfter(j); - after[i] = beans[j].memoryUsage(pmu.used, pmu.committed); + after[i] = beans[j].memoryUsage(pmu.getUsed(), pmu.getCommitted()); } } } Object[] extAttribute = new Object[1]; - extAttribute[0] = 1; // Number of GC threads. + extAttribute[0] = gcThreadCount(); - Target_com_sun_management_GcInfo targetGcInfo = new Target_com_sun_management_GcInfo(getGcInfoBuilder(), request.epoch, request.startTime, request.endTime, before, after, extAttribute); + Target_com_sun_management_GcInfo targetGcInfo = new Target_com_sun_management_GcInfo(getGcInfoBuilder(), request.getEpoch(), request.getStartTime(), request.getEndTime(), before, after, + extAttribute); gcInfo = SubstrateUtil.cast(targetGcInfo, GcInfo.class); GarbageCollectionNotificationInfo info = new GarbageCollectionNotificationInfo( getName(), - request.isIncremental ? ACTION_MINOR : ACTION_MAJOR, - request.cause.getName(), + request.isIncremental() ? ACTION_MINOR : ACTION_MAJOR, + request.getCause().getName(), gcInfo); CompositeData cd = GarbageCollectionNotifInfoCompositeData.toCompositeData(info); @@ -97,7 +99,7 @@ public void createNotification(GcNotificationRequest request) { Notification notif = new Notification(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION, getObjectName(), getNextSeqNumber(), - request.timestamp, + request.getTimestamp(), getName()); notif.setUserData(cd); @@ -129,4 +131,6 @@ public MBeanNotificationInfo[] getNotificationInfo() { public GcInfo getLastGcInfo() { return gcInfo; } + + protected abstract int gcThreadCount(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java index 7d662c56406f..7f41bdef8784 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java @@ -26,10 +26,11 @@ package com.oracle.svm.core.notification; +import com.oracle.svm.core.gc.AbstractMemoryPoolMXBean; +import com.oracle.svm.core.gc.MemoryPoolMXBeansProvider; import com.oracle.svm.core.heap.GCCause; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import com.oracle.svm.core.Uninterruptible; /** * This class stashes GC info during a safepoint until it can be dequeued to emit a notification at @@ -40,14 +41,14 @@ public class GcNotificationRequest { private final PoolMemoryUsage[] after; // Times since the VM started. - public long startTime; - public long endTime; + private long startTime; + private long endTime; // This is the system time that the request was sent. - public long timestamp; - public boolean isIncremental; - public GCCause cause; - public long epoch; + private long timestamp; + private boolean isIncremental; + private GCCause cause; + private long epoch; @Platforms(Platform.HOSTED_ONLY.class) GcNotificationRequest(int poolCount) { @@ -59,28 +60,68 @@ public class GcNotificationRequest { } } - @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static void beforeCollection(long startTime, GcNotificationRequest[] requests, int index) { + AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); + for (int i = 0; i < beans.length; i++) { + /* + * Always add the old generation pool even if we don't end up using it (since we don't + * know what type of collection will happen yet) + */ + requests[index].setPoolBefore(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); + } + requests[index].startTime = startTime; + } + + public static void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime, GcNotificationRequest[] requests, int index) { + AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); + for (int i = 0; i < beans.length; i++) { + requests[index].setPoolAfter(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); + } + requests[index].endTime = endTime; + requests[index].isIncremental = isIncremental; + requests[index].cause = cause; + requests[index].epoch = epoch; + requests[index].timestamp = System.currentTimeMillis(); + + } + void setPoolBefore(int index, long used, long committed, String name) { - before[index].used = used; - before[index].committed = committed; - before[index].name = name; + before[index].set(used, committed, name); } - @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) void setPoolAfter(int index, long used, long committed, String name) { - after[index].used = used; - after[index].committed = committed; - after[index].name = name; + after[index].set(used, committed, name); } - @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public PoolMemoryUsage getPoolBefore(int i) { return before[i]; } - @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public PoolMemoryUsage getPoolAfter(int i) { return after[i]; } + public boolean isIncremental() { + return isIncremental; + } + + public GCCause getCause() { + return cause; + } + + public long getEndTime() { + return endTime; + } + + public long getStartTime() { + return startTime; + } + + public long getTimestamp() { + return timestamp; + } + + public long getEpoch() { + return epoch; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java index 98d66894e697..36b5d3b41514 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java @@ -31,7 +31,6 @@ import org.graalvm.nativeimage.Platform; import com.oracle.svm.core.Uninterruptible; import jdk.graal.compiler.api.replacements.Fold; -import com.oracle.svm.core.gc.AbstractMemoryPoolMXBean; import com.oracle.svm.core.gc.MemoryPoolMXBeansProvider; import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.gc.AbstractGarbageCollectorMXBean; @@ -77,42 +76,20 @@ public static GcNotifier singleton() { /** Called during GC. */ public void beforeCollection(long startTime) { assert VMOperation.isInProgressAtSafepoint(); - - AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); - for (int i = 0; i < beans.length; i++) { - /* - * Always add the old generation pool even if we don't end up using it (since we don't - * know what type of collection will happen yet) - */ - requests[rear].setPoolBefore(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); - } - requests[rear].startTime = startTime; + GcNotificationRequest.beforeCollection(startTime, requests, rear); } /** Called during GC. */ public void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime) { assert VMOperation.isInProgressAtSafepoint(); - - AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); - for (int i = 0; i < beans.length; i++) { -// if (isIncremental && beans[i].getName().equals(OLD_GEN_SPACE)) { -// continue; -// } - requests[rear].setPoolAfter(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); - } - requests[rear].endTime = endTime; - requests[rear].isIncremental = isIncremental; - requests[rear].cause = cause; - requests[rear].epoch = epoch; - requests[rear].timestamp = System.currentTimeMillis(); - + GcNotificationRequest.afterCollection(isIncremental, cause, epoch, endTime, requests, rear); rear = incremented(rear); // If rear is now lapping front, then increment front to prevent overlap. Accept data loss. if (front == rear) { front = incremented(front); } - org.graalvm.nativeimage.ImageSingletons.lookup(NotificationThreadSupport.class).signalServiceThread(); + ImageSingletons.lookup(NotificationThreadSupport.class).signalServiceThread(); } @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @@ -141,7 +118,7 @@ public void sendNotification() { * NotificationRequest class that was dequeued. */ GarbageCollectorMXBean gcBean = null; - String targetBeanName = MemoryPoolMXBeansProvider.get().getCollectorName(currentRequest.isIncremental); + String targetBeanName = MemoryPoolMXBeansProvider.get().getCollectorName(currentRequest.isIncremental()); for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { if (bean.getName().equals(targetBeanName)) { gcBean = bean; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java index 2f03c09e0f89..f9e8b097e144 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java @@ -30,6 +30,9 @@ import com.oracle.svm.core.jdk.UninterruptibleUtils; import com.oracle.svm.core.locks.VMSemaphore; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.thread.PlatformThreads; +import com.oracle.svm.core.util.BasedOnJDKFile; + import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -38,6 +41,8 @@ * notifications. */ public class NotificationThread extends Thread { + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/hotspot/share/runtime/notificationThread.cpp#L41") \\ + private static final String THREAD_NAME = "Notification Thread"; private final UninterruptibleUtils.AtomicBoolean atomicNotify; private final VMSemaphore semaphore; private volatile boolean stopped; @@ -45,6 +50,8 @@ public class NotificationThread extends Thread { @Platforms(Platform.HOSTED_ONLY.class) @SuppressWarnings("this-escape") public NotificationThread() { + // Hotspot sets the notification thread to the system thread group. + super(PlatformThreads.singleton().systemGroup, THREAD_NAME); this.semaphore = new VMSemaphore("serviceThread"); this.atomicNotify = new UninterruptibleUtils.AtomicBoolean(false); setDaemon(true); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java index 39f487a25ccd..f7ba51c24268 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java @@ -28,7 +28,25 @@ /** Used from allocation free code and later converted to MemoryUsage. */ public class PoolMemoryUsage { - public long used; - public long committed; - public String name; + private long used; + private long committed; + private String name; + + public void set(long used, long committed, String name) { + this.used = used; + this.committed = committed; + this.name = name; + } + + public long getUsed() { + return used; + } + + public long getCommitted() { + return committed; + } + + public String getName() { + return name; + } } From cb33422e6b62030c631d45db6513de2fe48a942b Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Thu, 17 Oct 2024 13:43:05 -0400 Subject: [PATCH 06/12] use mutex+condition, fix getLastGcInfo, use RingBuffer --- .../CompleteGarbageCollectorMXBean.java | 7 ++ .../GenScavengeMemoryPoolMXBeans.java | 8 +- .../IncrementalGarbageCollectorMXBean.java | 7 ++ .../svm/core/collections/CircularQueue.java | 78 ++++++++++++++++ .../svm/core/collections/RingBuffer.java | 12 +-- .../gc/AbstractGarbageCollectorMXBean.java | 87 +++++++++++------- .../core/gc/MemoryPoolMXBeansProvider.java | 2 +- .../notification/GcNotificationRequest.java | 49 +++++++--- .../svm/core/notification/GcNotifier.java | 90 ++++++++++--------- .../core/notification/NotificationThread.java | 52 +++++++---- .../core/notification/PoolMemoryUsage.java | 6 ++ 11 files changed, 279 insertions(+), 119 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java index e90921727ee4..b41d91b7c0d3 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompleteGarbageCollectorMXBean.java @@ -28,6 +28,7 @@ import javax.management.ObjectName; +import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.gc.AbstractGarbageCollectorMXBean; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -82,4 +83,10 @@ public ObjectName getObjectName() { protected int gcThreadCount() { return 1; } + + @Override + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public boolean isIncremental() { + return false; + } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java index 4b592ae195f7..a58a83c8c757 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java @@ -71,10 +71,10 @@ public AbstractMemoryPoolMXBean[] getMXBeans() { return mxBeans; } - @Override - public String getCollectorName(boolean isIncremental) { - return isIncremental ? YOUNG_GEN_SCAVENGER : COMPLETE_SCAVENGER; - } +// @Override +// public String getCollectorName(boolean isIncremental) { +// return isIncremental ? YOUNG_GEN_SCAVENGER : COMPLETE_SCAVENGER; +// } @Override public void notifyBeforeCollection() { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java index 6309f4882265..03b2b6a8d1bd 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/IncrementalGarbageCollectorMXBean.java @@ -28,6 +28,7 @@ import javax.management.ObjectName; +import com.oracle.svm.core.Uninterruptible; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -81,4 +82,10 @@ public ObjectName getObjectName() { protected int gcThreadCount() { return 1; } + + @Override + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public boolean isIncremental() { + return true; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java new file mode 100644 index 000000000000..621ef2de8490 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.core.collections; + +import com.oracle.svm.core.Uninterruptible; + +import java.util.function.Supplier; + +/** Keeps the last n-1 entries. Entries at the head are removed first. */ +public final class CircularQueue extends RingBuffer { + private int head; + + public CircularQueue(int numEntries, Supplier supplier) { + super(numEntries, supplier); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public void advanceHead() { + head = nextIndex(head); + } + + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Override + public void advance() { + super.advance(); + // If rear is now lapping front, then increment front to prevent overlap. + if (head == pos) { + head = nextIndex(head); + } + } + + public T peekTail() { + T result = entries[pos]; + return result; + } + + public T dequeue() { + T result = entries[head]; + advanceHead(); + return result; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public T replaceHead(T newHead) { + T oldHead = entries[head]; + entries[head] = newHead; + return oldHead; + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public boolean isEmpty() { + return pos == head; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java index 8cd6b4df17f8..29fafa779f69 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java @@ -29,10 +29,10 @@ import com.oracle.svm.core.Uninterruptible; /** Keeps the last-n entries. */ -public final class RingBuffer { - private final T[] entries; - private int pos; - private boolean wrapped; +public class RingBuffer { + protected final T[] entries; + protected int pos; + protected boolean wrapped; public interface Consumer { void accept(Object context, T t); @@ -56,7 +56,7 @@ public int size() { } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private int nextIndex(int p) { + protected int nextIndex(int p) { return (p + 1) % entries.length; } @@ -67,7 +67,7 @@ public void append(T entry) { } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private void advance() { + protected void advance() { int posNext = nextIndex(pos); if (posNext <= pos) { wrapped = true; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java index 7b80ddf45e41..053e2c5866e4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java @@ -25,10 +25,13 @@ */ package com.oracle.svm.core.gc; +import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.util.BasedOnJDKFile; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.notification.GcNotifier; import com.oracle.svm.core.notification.GcNotificationRequest; +import com.oracle.svm.core.notification.HasGcNotificationSupport; import com.oracle.svm.core.notification.PoolMemoryUsage; import com.oracle.svm.core.notification.Target_com_sun_management_GcInfo; import com.oracle.svm.core.notification.Target_com_sun_management_internal_GcInfoBuilder; @@ -55,7 +58,6 @@ public abstract class AbstractGarbageCollectorMXBean extends NotificationEmitter private static final String ACTION_MAJOR = "end of major GC"; private Target_com_sun_management_internal_GcInfoBuilder gcInfoBuilder; - private volatile GcInfo gcInfo; private long seqNumber = 0; /** @@ -63,6 +65,51 @@ public abstract class AbstractGarbageCollectorMXBean extends NotificationEmitter * calls this method. */ public void createNotification(GcNotificationRequest request) { + + GcInfo gcInfo = getGcInfoFromRequest(request); + + GarbageCollectionNotificationInfo info = new GarbageCollectionNotificationInfo( + getName(), + request.isIncremental() ? ACTION_MINOR : ACTION_MAJOR, + request.getCause().getName(), + gcInfo); + + CompositeData cd = GarbageCollectionNotifInfoCompositeData.toCompositeData(info); + + Notification notif = new Notification(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION, + getObjectName(), + getNextSeqNumber(), + request.getTimestamp(), + getName()); + notif.setUserData(cd); + + sendNotification(notif); + } + + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + if (!HasGcNotificationSupport.get()) { + return new MBeanNotificationInfo[0]; + } + return new MBeanNotificationInfo[]{ + new MBeanNotificationInfo( + new String[]{GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION}, + "javax.management.Notification", + "GC Notification") + }; + } + + @Override + public GcInfo getLastGcInfo() { + if (!HasGcNotificationSupport.get()) { + return null; + } + GcNotificationRequest request = new GcNotificationRequest(); + getLastGcInfo(request); + return getGcInfoFromRequest(request); + } + + private GcInfo getGcInfoFromRequest(GcNotificationRequest request) { AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); String[] poolNames = getMemoryPoolNames(); @@ -86,24 +133,7 @@ public void createNotification(GcNotificationRequest request) { Target_com_sun_management_GcInfo targetGcInfo = new Target_com_sun_management_GcInfo(getGcInfoBuilder(), request.getEpoch(), request.getStartTime(), request.getEndTime(), before, after, extAttribute); - gcInfo = SubstrateUtil.cast(targetGcInfo, GcInfo.class); - - GarbageCollectionNotificationInfo info = new GarbageCollectionNotificationInfo( - getName(), - request.isIncremental() ? ACTION_MINOR : ACTION_MAJOR, - request.getCause().getName(), - gcInfo); - - CompositeData cd = GarbageCollectionNotifInfoCompositeData.toCompositeData(info); - - Notification notif = new Notification(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION, - getObjectName(), - getNextSeqNumber(), - request.getTimestamp(), - getName()); - notif.setUserData(cd); - - sendNotification(notif); + return SubstrateUtil.cast(targetGcInfo, GcInfo.class); } private Target_com_sun_management_internal_GcInfoBuilder getGcInfoBuilder() { @@ -117,20 +147,13 @@ private long getNextSeqNumber() { return ++seqNumber; } - @Override - public MBeanNotificationInfo[] getNotificationInfo() { - return new MBeanNotificationInfo[]{ - new MBeanNotificationInfo( - new String[]{GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION}, - "javax.management.Notification", - "GC Notification") - }; - } - - @Override - public GcInfo getLastGcInfo() { - return gcInfo; + @Uninterruptible(reason = "Avoid potential races with GC.") + private void getLastGcInfo(GcNotificationRequest request) { + GcNotifier.singleton().getLatestRequest(isIncremental()).copyTo(request); } protected abstract int gcThreadCount(); + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public abstract boolean isIncremental(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java index 6e0abf0b11de..f7f73e3b4e3c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java @@ -37,7 +37,7 @@ static MemoryPoolMXBeansProvider get() { AbstractMemoryPoolMXBean[] getMXBeans(); - String getCollectorName(boolean isIncremental); +// String getCollectorName(boolean isIncremental); void notifyBeforeCollection(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java index 7f41bdef8784..5db8be64fd7d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java @@ -26,11 +26,10 @@ package com.oracle.svm.core.notification; +import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.gc.AbstractMemoryPoolMXBean; import com.oracle.svm.core.gc.MemoryPoolMXBeansProvider; import com.oracle.svm.core.heap.GCCause; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; /** * This class stashes GC info during a safepoint until it can be dequeued to emit a notification at @@ -50,8 +49,8 @@ public class GcNotificationRequest { private GCCause cause; private long epoch; - @Platforms(Platform.HOSTED_ONLY.class) - GcNotificationRequest(int poolCount) { + public GcNotificationRequest() { + int poolCount = MemoryPoolMXBeansProvider.get().getMXBeans().length; before = new PoolMemoryUsage[poolCount]; after = new PoolMemoryUsage[poolCount]; for (int i = 0; i < poolCount; i++) { @@ -60,43 +59,65 @@ public class GcNotificationRequest { } } - public static void beforeCollection(long startTime, GcNotificationRequest[] requests, int index) { + public void beforeCollection(long startTime) { AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); for (int i = 0; i < beans.length; i++) { /* * Always add the old generation pool even if we don't end up using it (since we don't * know what type of collection will happen yet) */ - requests[index].setPoolBefore(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); + setPoolBefore(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); } - requests[index].startTime = startTime; + this.startTime = startTime; } - public static void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime, GcNotificationRequest[] requests, int index) { + public void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime) { AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); for (int i = 0; i < beans.length; i++) { - requests[index].setPoolAfter(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); + setPoolAfter(i, beans[i].getUsedBytes().rawValue(), beans[i].getCommittedBytes().rawValue(), beans[i].getName()); } - requests[index].endTime = endTime; - requests[index].isIncremental = isIncremental; - requests[index].cause = cause; - requests[index].epoch = epoch; - requests[index].timestamp = System.currentTimeMillis(); + this.endTime = endTime; + this.isIncremental = isIncremental; + this.cause = cause; + this.epoch = epoch; + this.timestamp = System.currentTimeMillis(); } + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) void setPoolBefore(int index, long used, long committed, String name) { before[index].set(used, committed, name); } + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) void setPoolAfter(int index, long used, long committed, String name) { after[index].set(used, committed, name); } + @Uninterruptible(reason = "Avoid pausing for a GC which could cause races.") + public void copyTo(GcNotificationRequest destination) { + destination.startTime = startTime; + destination.epoch = epoch; + destination.endTime = endTime; + destination.cause = cause; + destination.timestamp = timestamp; + destination.isIncremental = isIncremental; + + for (int index = 0; index < before.length; index++) { + destination.setPoolBefore(index, before[index].getUsed(), before[index].getCommitted(), before[index].getName()); + } + + for (int index = 0; index < after.length; index++) { + destination.setPoolAfter(index, after[index].getUsed(), after[index].getCommitted(), after[index].getName()); + } + } + + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public PoolMemoryUsage getPoolBefore(int i) { return before[i]; } + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public PoolMemoryUsage getPoolAfter(int i) { return after[i]; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java index 36b5d3b41514..9dfd5b56f479 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java @@ -26,17 +26,17 @@ package com.oracle.svm.core.notification; +import com.oracle.svm.core.collections.CircularQueue; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.Platform; import com.oracle.svm.core.Uninterruptible; import jdk.graal.compiler.api.replacements.Fold; -import com.oracle.svm.core.gc.MemoryPoolMXBeansProvider; import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.gc.AbstractGarbageCollectorMXBean; import com.oracle.svm.core.thread.VMOperation; -import java.lang.management.GarbageCollectorMXBean; +import com.sun.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; /** @@ -50,22 +50,21 @@ public class GcNotifier { // 5 is probably more than enough, although making the queue larger isn't a problem either. private static final int QUEUE_SIZE = 5; - private static int rear; - private static int front; - - GcNotificationRequest[] requests; + private final CircularQueue requestQueue; + // This is the request we are emitting a notification for GcNotificationRequest currentRequest; + /** This is cached to handle {@link GarbageCollectorMXBean#getLastGcInfo()} . */ + GcNotificationRequest latestRequestComplete; + /** This is cached to handle {@link GarbageCollectorMXBean#getLastGcInfo()} . */ + GcNotificationRequest latestRequestIncremental; @Platforms(Platform.HOSTED_ONLY.class) GcNotifier() { // Pre-allocate the queue so we can use it later from allocation free code. - requests = new GcNotificationRequest[QUEUE_SIZE]; - for (int i = 0; i < QUEUE_SIZE; i++) { - requests[i] = new GcNotificationRequest(MemoryPoolMXBeansProvider.get().getMXBeans().length); - } - currentRequest = new GcNotificationRequest(MemoryPoolMXBeansProvider.get().getMXBeans().length); - rear = 0; - front = 0; + requestQueue = new CircularQueue<>(QUEUE_SIZE, GcNotificationRequest::new); + currentRequest = new GcNotificationRequest(); + latestRequestComplete = new GcNotificationRequest(); + latestRequestIncremental = new GcNotificationRequest(); } @Fold @@ -76,73 +75,78 @@ public static GcNotifier singleton() { /** Called during GC. */ public void beforeCollection(long startTime) { assert VMOperation.isInProgressAtSafepoint(); - GcNotificationRequest.beforeCollection(startTime, requests, rear); + requestQueue.peekTail().beforeCollection(startTime); } /** Called during GC. */ public void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime) { assert VMOperation.isInProgressAtSafepoint(); - GcNotificationRequest.afterCollection(isIncremental, cause, epoch, endTime, requests, rear); - rear = incremented(rear); + requestQueue.peekTail().afterCollection(isIncremental, cause, epoch, endTime); - // If rear is now lapping front, then increment front to prevent overlap. Accept data loss. - if (front == rear) { - front = incremented(front); + if (isIncremental) { + requestQueue.peekTail().copyTo(latestRequestIncremental); + } else { + requestQueue.peekTail().copyTo(latestRequestComplete); } + + requestQueue.advance(); ImageSingletons.lookup(NotificationThreadSupport.class).signalServiceThread(); } - @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - private static int incremented(int value) { - return (value + 1) % QUEUE_SIZE; + @Uninterruptible(reason = "Avoid pausing for a GC which could cause races.") + public boolean hasRequest() { + return !requestQueue.isEmpty(); } /** * Called by the notification thread. Sends a notification if any are queued. If none are * queued, then return immediately. */ - public void sendNotification() { + void sendNotification() { assert !VMOperation.isInProgressAtSafepoint(); - /* - * We don't need to drain all requests since signals queue at the semaphore level. The rest - * of the requests will be handled in subsequent calls to this method by the sevice thread. - */ if (!updateCurrentRequest()) { // No new requests. return; } - /* - * Iterate to figure out which gc bean to send notifications too. Match with name in - * NotificationRequest class that was dequeued. - */ + GarbageCollectorMXBean gcBean = getGarbageCollectorMXBean(currentRequest.isIncremental()); + + ((AbstractGarbageCollectorMXBean) gcBean).createNotification(currentRequest); + } + + private GarbageCollectorMXBean getGarbageCollectorMXBean(boolean isIncremental) { GarbageCollectorMXBean gcBean = null; - String targetBeanName = MemoryPoolMXBeansProvider.get().getCollectorName(currentRequest.isIncremental()); for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { - if (bean.getName().equals(targetBeanName)) { + if (((AbstractGarbageCollectorMXBean) bean).isIncremental() == isIncremental) { gcBean = bean; break; } } - assert (gcBean != null); - ((AbstractGarbageCollectorMXBean) gcBean).createNotification(currentRequest); + return gcBean; } /** Called by the notification thread. */ - @Uninterruptible(reason = "Avoid pausing for a GC safepoint with could cause races with pushes to the request queue.") + @Uninterruptible(reason = "Avoid pausing for a GC safepoint which could cause races with pushes to the request queue.") private boolean updateCurrentRequest() { // If there's nothing to read, return immediately. - if (front == rear) { + if (!hasRequest()) { return false; } - // Copy the data to avoid data being overwritten by new writes to the queue. - GcNotificationRequest temp = currentRequest; - currentRequest = requests[front]; - requests[front] = temp; - - front = incremented(front); + // Move the data to avoid data being overwritten by new writes to the queue. + currentRequest = requestQueue.replaceHead(currentRequest); + requestQueue.advanceHead(); return true; } + + /** Called by notification thread. */ + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public GcNotificationRequest getLatestRequest(boolean isIncremental) { + assert !VMOperation.isInProgressAtSafepoint(); + if (isIncremental) { + return latestRequestIncremental; + } + return latestRequestComplete; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java index f9e8b097e144..4e10a52466dd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java @@ -26,9 +26,8 @@ package com.oracle.svm.core.notification; -import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.jdk.UninterruptibleUtils; -import com.oracle.svm.core.locks.VMSemaphore; +import com.oracle.svm.core.locks.VMCondition; +import com.oracle.svm.core.locks.VMMutex; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.thread.PlatformThreads; import com.oracle.svm.core.util.BasedOnJDKFile; @@ -41,10 +40,10 @@ * notifications. */ public class NotificationThread extends Thread { - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/hotspot/share/runtime/notificationThread.cpp#L41") \\ + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/hotspot/share/runtime/notificationThread.cpp#L41") // private static final String THREAD_NAME = "Notification Thread"; - private final UninterruptibleUtils.AtomicBoolean atomicNotify; - private final VMSemaphore semaphore; + private final VMMutex mutex; + private final VMCondition condition; private volatile boolean stopped; @Platforms(Platform.HOSTED_ONLY.class) @@ -52,8 +51,8 @@ public class NotificationThread extends Thread { public NotificationThread() { // Hotspot sets the notification thread to the system thread group. super(PlatformThreads.singleton().systemGroup, THREAD_NAME); - this.semaphore = new VMSemaphore("serviceThread"); - this.atomicNotify = new UninterruptibleUtils.AtomicBoolean(false); + this.mutex = new VMMutex("serviceThread"); + this.condition = new VMCondition(mutex); setDaemon(true); } @@ -61,24 +60,39 @@ public NotificationThread() { @Override public void run() { while (!stopped) { - if (await()) { - if (HasGcNotificationSupport.get()) { - GcNotifier.singleton().sendNotification(); - } - // In the future, we may want to do other things here too. + if (!hasRequests()) { + await(); } + if (HasGcNotificationSupport.get()) { + GcNotifier.singleton().sendNotification(); + } + // In the future, we may want to do other things here too. + } + } + + private void await() { + mutex.lock(); + try { + while (!hasRequests() && !stopped) { + condition.block(); + } + } finally { + mutex.unlock(); } } - private boolean await() { - semaphore.await(); - return atomicNotify.compareAndSet(true, false); + private boolean hasRequests() { + // In the future, we may check for other pending requests here too. + return GcNotifier.singleton().hasRequest(); } - @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public void signal() { - atomicNotify.set(true); - semaphore.signal(); + mutex.lock(); + try { + condition.signal(); + } finally { + mutex.unlock(); + } } public void shutdown() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java index f7ba51c24268..35011a951e5b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/PoolMemoryUsage.java @@ -26,26 +26,32 @@ package com.oracle.svm.core.notification; +import com.oracle.svm.core.Uninterruptible; + /** Used from allocation free code and later converted to MemoryUsage. */ public class PoolMemoryUsage { private long used; private long committed; private String name; + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public void set(long used, long committed, String name) { this.used = used; this.committed = committed; this.name = name; } + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public long getUsed() { return used; } + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public long getCommitted() { return committed; } + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public String getName() { return name; } From 53024069a342a27cdc8cf8dc5ce8f1adbf4f3282 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 18 Oct 2024 09:47:47 -0400 Subject: [PATCH 07/12] BasedOnJdkFile. Fix signalling --- .../svm/core/collections/CircularQueue.java | 13 ++++++-- .../gc/AbstractGarbageCollectorMXBean.java | 33 +++++++++++-------- .../svm/core/notification/GcNotifier.java | 22 ++++++++----- .../core/notification/NotificationThread.java | 17 ++++++---- .../NotificationThreadSupport.java | 2 +- 5 files changed, 54 insertions(+), 33 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java index 621ef2de8490..ba64baac0065 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java @@ -54,8 +54,7 @@ public void advance() { } public T peekTail() { - T result = entries[pos]; - return result; + return entries[pos]; } public T dequeue() { @@ -70,6 +69,16 @@ public T replaceHead(T newHead) { entries[head] = newHead; return oldHead; } +// +// @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) +// public T peekHead() { +// return entries[head]; +// } +// @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) +// public void setHead(T newHead) { +// entries[head] = newHead; +// } + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public boolean isEmpty() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java index 053e2c5866e4..2e4378d30391 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java @@ -48,13 +48,14 @@ import javax.management.openmbean.CompositeData; import java.lang.management.MemoryUsage; +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/jdk.management/share/classes/com/sun/management/internal/GarbageCollectorExtImpl.java") // public abstract class AbstractGarbageCollectorMXBean extends NotificationEmitterSupport implements GarbageCollectorMXBean, NotificationEmitter { - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/hotspot/share/gc/serial/serialHeap.cpp#L451") // + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/hotspot/share/gc/serial/serialHeap.cpp#L451") // private static final String ACTION_MINOR = "end of minor GC"; - @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/hotspot/share/gc/serial/serialHeap.cpp#L718") // + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/hotspot/share/gc/serial/serialHeap.cpp#L718") // private static final String ACTION_MAJOR = "end of major GC"; private Target_com_sun_management_internal_GcInfoBuilder gcInfoBuilder; @@ -64,10 +65,20 @@ public abstract class AbstractGarbageCollectorMXBean extends NotificationEmitter * Use the data taken from the request queue to populate MemoryUsage. The notification thread * calls this method. */ + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/jdk.management/share/classes/com/sun/management/internal/GarbageCollectorExtImpl.java#L93-L116") // public void createNotification(GcNotificationRequest request) { + if (!hasListeners()) { + return; + } GcInfo gcInfo = getGcInfoFromRequest(request); + Notification notif = new Notification(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION, + getObjectName(), + getNextSeqNumber(), + request.getTimestamp(), + getName()); + GarbageCollectionNotificationInfo info = new GarbageCollectionNotificationInfo( getName(), request.isIncremental() ? ACTION_MINOR : ACTION_MAJOR, @@ -75,17 +86,11 @@ public void createNotification(GcNotificationRequest request) { gcInfo); CompositeData cd = GarbageCollectionNotifInfoCompositeData.toCompositeData(info); - - Notification notif = new Notification(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION, - getObjectName(), - getNextSeqNumber(), - request.getTimestamp(), - getName()); notif.setUserData(cd); - sendNotification(notif); } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/jdk.management/share/classes/com/sun/management/internal/GarbageCollectorExtImpl.java#L80-L86") // @Override public MBeanNotificationInfo[] getNotificationInfo() { if (!HasGcNotificationSupport.get()) { @@ -109,6 +114,11 @@ public GcInfo getLastGcInfo() { return getGcInfoFromRequest(request); } + @Uninterruptible(reason = "Avoid potential races with GC.") + private void getLastGcInfo(GcNotificationRequest request) { + GcNotifier.singleton().getLatestRequest(isIncremental()).copyTo(request); + } + private GcInfo getGcInfoFromRequest(GcNotificationRequest request) { AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); @@ -147,11 +157,6 @@ private long getNextSeqNumber() { return ++seqNumber; } - @Uninterruptible(reason = "Avoid potential races with GC.") - private void getLastGcInfo(GcNotificationRequest request) { - GcNotifier.singleton().getLatestRequest(isIncremental()).copyTo(request); - } - protected abstract int gcThreadCount(); @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java index 9dfd5b56f479..6fead5ccab66 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java @@ -27,18 +27,21 @@ package com.oracle.svm.core.notification; import com.oracle.svm.core.collections.CircularQueue; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.Platform; -import com.oracle.svm.core.Uninterruptible; -import jdk.graal.compiler.api.replacements.Fold; import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.gc.AbstractGarbageCollectorMXBean; import com.oracle.svm.core.thread.VMOperation; +import com.oracle.svm.core.util.BasedOnJDKFile; +import com.oracle.svm.core.Uninterruptible; -import com.sun.management.GarbageCollectorMXBean; +import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; +import jdk.graal.compiler.api.replacements.Fold; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.Platform; + /** * This class keeps a circular queue of requests to later use when emitting GC notifications. The * requests are pre-allocated so that they can be used during a GC. @@ -47,9 +50,10 @@ * requests to generate notifications when the notification thread handles signals outside of * safepoints. */ +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/hotspot/share/services/gcNotifier.cpp") // public class GcNotifier { // 5 is probably more than enough, although making the queue larger isn't a problem either. - private static final int QUEUE_SIZE = 5; + private static final int QUEUE_SIZE = 3; private final CircularQueue requestQueue; // This is the request we are emitting a notification for GcNotificationRequest currentRequest; @@ -90,7 +94,7 @@ public void afterCollection(boolean isIncremental, GCCause cause, long epoch, lo } requestQueue.advance(); - ImageSingletons.lookup(NotificationThreadSupport.class).signalServiceThread(); + ImageSingletons.lookup(NotificationThreadSupport.class).signalNotificationThread(); } @Uninterruptible(reason = "Avoid pausing for a GC which could cause races.") @@ -102,6 +106,7 @@ public boolean hasRequest() { * Called by the notification thread. Sends a notification if any are queued. If none are * queued, then return immediately. */ + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/hotspot/share/services/gcNotifier.cpp#L191-L225") // void sendNotification() { assert !VMOperation.isInProgressAtSafepoint(); @@ -143,7 +148,6 @@ private boolean updateCurrentRequest() { /** Called by notification thread. */ @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public GcNotificationRequest getLatestRequest(boolean isIncremental) { - assert !VMOperation.isInProgressAtSafepoint(); if (isIncremental) { return latestRequestIncremental; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java index 4e10a52466dd..d14c4700d6e6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java @@ -31,6 +31,8 @@ import com.oracle.svm.core.log.Log; import com.oracle.svm.core.thread.PlatformThreads; import com.oracle.svm.core.util.BasedOnJDKFile; +import com.oracle.svm.core.util.TimeUtils; +import com.oracle.svm.core.Uninterruptible; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -57,6 +59,7 @@ public NotificationThread() { } /** Awakens to send notifications asynchronously. */ + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+19/src/hotspot/share/runtime/notificationThread.cpp#L52-L91") // @Override public void run() { while (!stopped) { @@ -74,25 +77,25 @@ private void await() { mutex.lock(); try { while (!hasRequests() && !stopped) { - condition.block(); + /* + * It's possible for a signal to get lost here if a GC occurs after we check + * hasRequests() but before we block. So wait a maximum of 3s as a precaution. + */ + condition.block(TimeUtils.millisToNanos(3000)); } } finally { mutex.unlock(); } } + @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private boolean hasRequests() { // In the future, we may check for other pending requests here too. return GcNotifier.singleton().hasRequest(); } public void signal() { - mutex.lock(); - try { - condition.signal(); - } finally { - mutex.unlock(); - } + condition.signal(); } public void shutdown() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadSupport.java index d04af9e04a9f..4c64f33eb0c3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThreadSupport.java @@ -48,7 +48,7 @@ void initialize() { notificationThread.start(); } - public void signalServiceThread() { + public void signalNotificationThread() { notificationThread.signal(); } From eb28fe671a173298ac62c0ce3a6efc0d32e2772a Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 18 Oct 2024 10:38:18 -0400 Subject: [PATCH 08/12] small cleanup --- .../svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java | 5 ----- .../com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java | 2 -- 2 files changed, 7 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java index a58a83c8c757..2eebd70697f1 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java @@ -71,11 +71,6 @@ public AbstractMemoryPoolMXBean[] getMXBeans() { return mxBeans; } -// @Override -// public String getCollectorName(boolean isIncremental) { -// return isIncremental ? YOUNG_GEN_SCAVENGER : COMPLETE_SCAVENGER; -// } - @Override public void notifyBeforeCollection() { for (AbstractMemoryPoolMXBean mxBean : mxBeans) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java index f7f73e3b4e3c..787e23ecaf3f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/MemoryPoolMXBeansProvider.java @@ -37,8 +37,6 @@ static MemoryPoolMXBeansProvider get() { AbstractMemoryPoolMXBean[] getMXBeans(); -// String getCollectorName(boolean isIncremental); - void notifyBeforeCollection(); void notifyAfterCollection(); From 4c09b4f354dfa20358763912c24fc76b3fa3f423 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 18 Oct 2024 11:41:39 -0400 Subject: [PATCH 09/12] gate fixes --- .../oracle/svm/core/collections/CircularQueue.java | 11 ----------- .../svm/core/notification/GcNotificationRequest.java | 2 ++ .../com/oracle/svm/core/notification/GcNotifier.java | 10 +++++++--- .../svm/core/notification/NotificationThread.java | 2 +- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java index ba64baac0065..3a77af025419 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/CircularQueue.java @@ -42,7 +42,6 @@ public void advanceHead() { head = nextIndex(head); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @Override public void advance() { @@ -69,16 +68,6 @@ public T replaceHead(T newHead) { entries[head] = newHead; return oldHead; } -// -// @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) -// public T peekHead() { -// return entries[head]; -// } -// @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) -// public void setHead(T newHead) { -// entries[head] = newHead; -// } - @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public boolean isEmpty() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java index 5db8be64fd7d..4a56ef7a3858 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java @@ -59,6 +59,7 @@ public GcNotificationRequest() { } } + @SuppressWarnings("hiding") public void beforeCollection(long startTime) { AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); for (int i = 0; i < beans.length; i++) { @@ -71,6 +72,7 @@ public void beforeCollection(long startTime) { this.startTime = startTime; } + @SuppressWarnings("hiding") public void afterCollection(boolean isIncremental, GCCause cause, long epoch, long endTime) { AbstractMemoryPoolMXBean[] beans = MemoryPoolMXBeansProvider.get().getMXBeans(); for (int i = 0; i < beans.length; i++) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java index 6fead5ccab66..25cad7521e54 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotifier.java @@ -57,9 +57,13 @@ public class GcNotifier { private final CircularQueue requestQueue; // This is the request we are emitting a notification for GcNotificationRequest currentRequest; - /** This is cached to handle {@link GarbageCollectorMXBean#getLastGcInfo()} . */ + /** + * This is cached to handle {@link com.sun.management.GarbageCollectorMXBean#getLastGcInfo()} . + */ GcNotificationRequest latestRequestComplete; - /** This is cached to handle {@link GarbageCollectorMXBean#getLastGcInfo()} . */ + /** + * This is cached to handle {@link com.sun.management.GarbageCollectorMXBean#getLastGcInfo()} . + */ GcNotificationRequest latestRequestIncremental; @Platforms(Platform.HOSTED_ONLY.class) @@ -120,7 +124,7 @@ void sendNotification() { ((AbstractGarbageCollectorMXBean) gcBean).createNotification(currentRequest); } - private GarbageCollectorMXBean getGarbageCollectorMXBean(boolean isIncremental) { + private static GarbageCollectorMXBean getGarbageCollectorMXBean(boolean isIncremental) { GarbageCollectorMXBean gcBean = null; for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { if (((AbstractGarbageCollectorMXBean) bean).isIncremental() == isIncremental) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java index d14c4700d6e6..01874c4b18e8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/NotificationThread.java @@ -89,7 +89,7 @@ private void await() { } @Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - private boolean hasRequests() { + private static boolean hasRequests() { // In the future, we may check for other pending requests here too. return GcNotifier.singleton().hasRequest(); } From df2d4b2412fd040c8c5c9879079017195b97b3c0 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 18 Oct 2024 11:50:44 -0400 Subject: [PATCH 10/12] style --- .../svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java | 1 - 1 file changed, 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java index 2eebd70697f1..4ea4b4bc241f 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java @@ -27,7 +27,6 @@ import java.lang.management.MemoryUsage; - import com.oracle.svm.core.gc.AbstractMemoryPoolMXBean; import com.oracle.svm.core.gc.MemoryPoolMXBeansProvider; import org.graalvm.nativeimage.Platform; From 71f75a8a0acc4760216ada607ee4be1facd30eb2 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 18 Oct 2024 13:35:15 -0400 Subject: [PATCH 11/12] Fix getLastGcInfo when no GC has happened. update tests. --- .../gc/AbstractGarbageCollectorMXBean.java | 5 +++ .../notification/GcNotificationRequest.java | 1 + .../svm/test/service/GcNotificationTests.java | 36 ++++++++++++++++--- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java index 2e4378d30391..b6cdc2c9e700 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/gc/AbstractGarbageCollectorMXBean.java @@ -111,6 +111,10 @@ public GcInfo getLastGcInfo() { } GcNotificationRequest request = new GcNotificationRequest(); getLastGcInfo(request); + if (request.getEpoch() < 0) { + // This collector has not done any collections yet. + return null; + } return getGcInfoFromRequest(request); } @@ -136,6 +140,7 @@ private GcInfo getGcInfoFromRequest(GcNotificationRequest request) { after[i] = beans[j].memoryUsage(pmu.getUsed(), pmu.getCommitted()); } } + assert before[i] != null && after[i] != null; } Object[] extAttribute = new Object[1]; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java index 4a56ef7a3858..0c7b0167ded0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/GcNotificationRequest.java @@ -57,6 +57,7 @@ public GcNotificationRequest() { before[i] = new PoolMemoryUsage(); after[i] = new PoolMemoryUsage(); } + epoch = -1; // Indicate no collections happened yet. } @SuppressWarnings("hiding") diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java index d21f8ea67366..c5d7c17fc767 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/service/GcNotificationTests.java @@ -37,11 +37,36 @@ import javax.management.openmbean.CompositeData; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; +import java.util.Set; import static org.junit.Assert.assertTrue; public class GcNotificationTests { + @Test + public void testGetLastGcInfo() { + System.gc(); + for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) { + if (!(gcBean instanceof NotificationEmitter)) { + continue; + } + GcInfo lastGcInfo = ((com.sun.management.GarbageCollectorMXBean) gcBean).getLastGcInfo(); + if (lastGcInfo != null) { + assertTrue(lastGcInfo.getStartTime() > 0); + assertTrue(lastGcInfo.getId() >= 0); + assertTrue(lastGcInfo.getDuration() >= 0); // Precision is 1 ms. + + Set poolNames = lastGcInfo.getMemoryUsageBeforeGc().keySet(); + for (String poolName : poolNames) { + long before = lastGcInfo.getMemoryUsageBeforeGc().get(poolName).getUsed(); + long after = lastGcInfo.getMemoryUsageAfterGc().get(poolName).getUsed(); + assertTrue(before >= 0); + assertTrue(after >= 0); + } + } + } + } + @Test public void testListenerRegistration() { int count = 0; @@ -104,10 +129,13 @@ public void handleNotification(Notification notification, Object handback) { GcInfo gcInfo = notificationInfo.getGcInfo(); assertTrue(gcInfo != null); assertTrue(gcInfo.getDuration() >= 0); // Precision is 1 ms. - long before = gcInfo.getMemoryUsageBeforeGc().get("eden space").getUsed(); - long after = gcInfo.getMemoryUsageAfterGc().get("eden space").getUsed(); - assertTrue(before > 0); - assertTrue(before >= after); + Set poolNames = gcInfo.getMemoryUsageBeforeGc().keySet(); + for (String poolName : poolNames) { + long before = gcInfo.getMemoryUsageBeforeGc().get(poolName).getUsed(); + long after = gcInfo.getMemoryUsageAfterGc().get(poolName).getUsed(); + assertTrue(before >= 0); + assertTrue(after >= 0); + } if (notificationInfo.getGcCause().equals("java.lang.System.gc()")) { signalFinished(); From 09890a7887eb0fbfc8aaca844a0d98084b4a015e Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 18 Oct 2024 14:56:03 -0400 Subject: [PATCH 12/12] clean suite.py --- substratevm/mx.substratevm/suite.py | 3 --- .../svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java | 2 +- .../src/com/oracle/svm/core/collections/RingBuffer.java | 2 +- .../oracle/svm/core/notification/HasGcNotificationSupport.java | 3 ++- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 0cba625d158e..9ef89209542e 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -364,9 +364,6 @@ "java.management": [ "sun.management", ], - "jdk.management": [ - "com.sun.management.internal" - ], "jdk.internal.vm.ci" : [ "jdk.vm.ci.code", "jdk.vm.ci.meta" diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java index 4ea4b4bc241f..dc96af5ccffd 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeMemoryPoolMXBeans.java @@ -44,7 +44,7 @@ public class GenScavengeMemoryPoolMXBeans implements MemoryPoolMXBeansProvider { static final String EDEN_SPACE = "eden space"; static final String SURVIVOR_SPACE = "survivor space"; - public static final String OLD_GEN_SPACE = "old generation space"; + static final String OLD_GEN_SPACE = "old generation space"; static final String EPSILON_HEAP = "epsilon heap"; private final AbstractMemoryPoolMXBean[] mxBeans; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java index 29fafa779f69..244a0379082d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/RingBuffer.java @@ -32,7 +32,7 @@ public class RingBuffer { protected final T[] entries; protected int pos; - protected boolean wrapped; + private boolean wrapped; public interface Consumer { void accept(Object context, T t); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/HasGcNotificationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/HasGcNotificationSupport.java index c40ecb17acc8..2857b5160770 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/HasGcNotificationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/notification/HasGcNotificationSupport.java @@ -26,9 +26,10 @@ package com.oracle.svm.core.notification; +import com.oracle.svm.core.VMInspectionOptions; + import java.util.function.BooleanSupplier; -import com.oracle.svm.core.VMInspectionOptions; import jdk.graal.compiler.api.replacements.Fold; public class HasGcNotificationSupport implements BooleanSupplier {