diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index afbb8f070326..c582d59de57a 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -4,6 +4,7 @@ This changelog summarizes major changes to GraalVM Native Image. ## GraalVM for JDK 22 (Internal Version 24.0.0) * (GR-48304) Red Hat added support for the JFR event ThreadAllocationStatistics. +* (GR-48343) Red Hat added support for the JFR events AllocationRequiringGC and SystemGC. ## GraalVM for JDK 21 (Internal Version 23.1.0) * (GR-35746) Lower the default aligned chunk size from 1 MB to 512 KB for the serial and epsilon GCs, reducing memory usage and image size in many cases. diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java index dc8a0018e351..891505c08fc6 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java @@ -35,7 +35,6 @@ import com.oracle.svm.core.SubstrateGCOptions; import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.heap.PhysicalMemory; import com.oracle.svm.core.heap.ReferenceAccess; import com.oracle.svm.core.jdk.UninterruptibleUtils; @@ -96,8 +95,9 @@ public boolean shouldCollectOnAllocation() { } @Override - public boolean shouldCollectOnRequest(GCCause cause, boolean fullGC) { - return cause == GCCause.JavaLangSystemGC && !SubstrateGCOptions.DisableExplicitGC.getValue(); + public boolean shouldCollectOnHint(boolean fullGC) { + /* Collection hints are not supported. */ + return false; } @Fold diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/BasicCollectionPolicies.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/BasicCollectionPolicies.java index 5ccb2f470eee..1de09da02942 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/BasicCollectionPolicies.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/BasicCollectionPolicies.java @@ -63,8 +63,9 @@ public boolean shouldCollectOnAllocation() { } @Override - public boolean shouldCollectOnRequest(GCCause cause, boolean fullGC) { - return cause == GCCause.JavaLangSystemGC && !SubstrateGCOptions.DisableExplicitGC.getValue(); + public boolean shouldCollectOnHint(boolean fullGC) { + /* Collection hints are not supported. */ + return false; } @Override diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CollectionPolicy.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CollectionPolicy.java index 2dd29291d6b1..8b235331c44e 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CollectionPolicy.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CollectionPolicy.java @@ -122,11 +122,10 @@ static boolean shouldCollectYoungGenSeparately(boolean defaultValue) { boolean shouldCollectOnAllocation(); /** - * Return true if a user-requested GC (e.g., call to {@link System#gc()} or - * {@link org.graalvm.compiler.serviceprovider.GraalServices#notifyLowMemoryPoint(boolean)}) - * should be performed. + * Called when an application provides a hint to the GC that it might be a good time to do a + * collection. Returns true if the GC decides to do a collection. */ - boolean shouldCollectOnRequest(GCCause cause, boolean fullGC); + boolean shouldCollectOnHint(boolean fullGC); /** * At a safepoint, decides whether to do a complete collection (returning {@code true}) or an diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCAccounting.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCAccounting.java index 0a861933dd07..4d83d99b2547 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCAccounting.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCAccounting.java @@ -78,6 +78,7 @@ public long getIncrementalCollectionTotalNanos() { return incrementalCollectionTotalNanos; } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public long getCompleteCollectionCount() { return completeCollectionCount; } 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 818188e9a5d4..a5caf759d016 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 @@ -81,6 +81,7 @@ import com.oracle.svm.core.jdk.RuntimeSupport; import com.oracle.svm.core.jfr.JfrGCWhen; import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.events.AllocationRequiringGCEvent; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.os.CommittedMemoryProvider; import com.oracle.svm.core.snippets.ImplicitExceptions; @@ -144,12 +145,13 @@ public void collect(GCCause cause) { collect(cause, false); } - public void maybeCollectOnAllocation() { + public void maybeCollectOnAllocation(UnsignedWord allocationSize) { boolean outOfMemory = false; if (hasNeverCollectPolicy()) { UnsignedWord edenUsed = HeapImpl.getAccounting().getEdenUsedBytes(); outOfMemory = edenUsed.aboveThan(GCImpl.getPolicy().getMaximumHeapSize()); } else if (getPolicy().shouldCollectOnAllocation()) { + AllocationRequiringGCEvent.emit(getCollectionEpoch(), allocationSize); outOfMemory = collectWithoutAllocating(GenScavengeGCCause.OnAllocation, false); } if (outOfMemory) { @@ -158,9 +160,9 @@ public void maybeCollectOnAllocation() { } @Override - public void maybeCauseUserRequestedCollection(GCCause cause, boolean fullGC) { - if (policy.shouldCollectOnRequest(cause, fullGC)) { - collect(cause, fullGC); + public void collectionHint(boolean fullGC) { + if (policy.shouldCollectOnHint(fullGC)) { + collect(GCCause.HintedGC, fullGC); } } @@ -183,6 +185,7 @@ boolean collectWithoutAllocating(GCCause cause, boolean forceFullGC) { UnmanagedMemoryUtil.fill((Pointer) data, WordFactory.unsigned(size), (byte) 0); data.setCauseId(cause.getId()); data.setRequestingEpoch(getCollectionEpoch()); + data.setCompleteCollectionCount(GCImpl.getAccounting().getCompleteCollectionCount()); data.setRequestingNanoTime(System.nanoTime()); data.setForceFullGC(forceFullGC); enqueueCollectOperation(data); @@ -197,7 +200,8 @@ private void enqueueCollectOperation(CollectionVMOperationData data) { /** The body of the VMOperation to do the collection. */ private void collectOperation(CollectionVMOperationData data) { assert VMOperation.isGCInProgress(); - assert getCollectionEpoch().equal(data.getRequestingEpoch()); + assert getCollectionEpoch().equal(data.getRequestingEpoch()) || + data.getForceFullGC() && GCImpl.getAccounting().getCompleteCollectionCount() == data.getCompleteCollectionCount() : "unnecessary GC?"; timers.mutator.closeAt(data.getRequestingNanoTime()); timers.resetAllExceptMutator(); @@ -1205,7 +1209,12 @@ private static void collect(CollectionVMOperationData data) { @Override protected boolean hasWork(NativeVMOperationData data) { CollectionVMOperationData d = (CollectionVMOperationData) data; - return HeapImpl.getGCImpl().getCollectionEpoch().equal(d.getRequestingEpoch()); + if (d.getForceFullGC()) { + /* Skip if another full GC happened in the meanwhile. */ + return GCImpl.getAccounting().getCompleteCollectionCount() == d.getCompleteCollectionCount(); + } + /* Skip if any other GC happened in the meanwhile. */ + return GCImpl.getGCImpl().getCollectionEpoch().equal(d.getRequestingEpoch()); } } @@ -1235,6 +1244,12 @@ private interface CollectionVMOperationData extends NativeVMOperationData { @RawField void setForceFullGC(boolean value); + @RawField + long getCompleteCollectionCount(); + + @RawField + void setCompleteCollectionCount(long value); + @RawField boolean getOutOfMemory(); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java index abd387622f11..4587cedad8f7 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java @@ -48,6 +48,7 @@ import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk; import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegistry; import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext; +import com.oracle.svm.core.SubstrateGCOptions; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.Uninterruptible; @@ -73,6 +74,8 @@ import com.oracle.svm.core.heap.RuntimeCodeInfoGCSupport; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicReference; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.events.SystemGCEvent; import com.oracle.svm.core.locks.VMCondition; import com.oracle.svm.core.locks.VMMutex; import com.oracle.svm.core.log.Log; @@ -941,6 +944,10 @@ private long maxMemory() { @Substitute private void gc() { - GCImpl.getGCImpl().maybeCauseUserRequestedCollection(GCCause.JavaLangSystemGC, true); + if (!SubstrateGCOptions.DisableExplicitGC.getValue()) { + long startTicks = JfrTicks.elapsedTicks(); + GCImpl.getGCImpl().collectCompletely(GCCause.JavaLangSystemGC); + SystemGCEvent.emit(startTicks, false); + } } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/LibGraalCollectionPolicy.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/LibGraalCollectionPolicy.java index d82bc8d953dc..7e4aed6af194 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/LibGraalCollectionPolicy.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/LibGraalCollectionPolicy.java @@ -73,20 +73,18 @@ public static final class Options { * size is lower but the hinted GC is more often. */ @Override - public boolean shouldCollectOnRequest(GCCause cause, boolean fullGC) { - if (cause == GCCause.HintedGC) { - guaranteeSizeParametersInitialized(); - UnsignedWord edenUsedBytes = HeapImpl.getAccounting().getEdenUsedBytes(); - if (fullGC) { - // For full GC request, we slightly lower the threshold to increase their - // probability to be performed, as they are supposed to be issued at the lowest - // memory usage point. - edenUsedBytes = edenUsedBytes.add(FULL_GC_BONUS); - } - return edenUsedBytes.aboveOrEqual(WordFactory.unsigned(Options.ExpectedEdenSize.getValue())) || - (UnsignedUtils.toDouble(edenUsedBytes) / UnsignedUtils.toDouble(edenSize) >= Options.UsedEdenProportionThreshold.getValue()); + public boolean shouldCollectOnHint(boolean fullGC) { + guaranteeSizeParametersInitialized(); + UnsignedWord edenUsedBytes = HeapImpl.getAccounting().getEdenUsedBytes(); + if (fullGC) { + /* + * For full GC request, we slightly lower the threshold to increase their probability to + * be performed, as they are supposed to be issued at the lowest memory usage point. + */ + edenUsedBytes = edenUsedBytes.add(FULL_GC_BONUS); } - return super.shouldCollectOnRequest(cause, fullGC); + return edenUsedBytes.aboveOrEqual(WordFactory.unsigned(Options.ExpectedEdenSize.getValue())) || + (UnsignedUtils.toDouble(edenUsedBytes) / UnsignedUtils.toDouble(edenSize) >= Options.UsedEdenProportionThreshold.getValue()); } @Override diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java index be84e80e8d16..c2557e21d5bb 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java @@ -230,14 +230,15 @@ private static Object slowPathNewInstance(Word objectHeader) { private static Object slowPathNewInstanceWithoutAllocating(DynamicHub hub) { DeoptTester.disableDeoptTesting(); long startTicks = JfrTicks.elapsedTicks(); + UnsignedWord size = LayoutEncoding.getPureInstanceAllocationSize(hub.getLayoutEncoding()); try { HeapImpl.exitIfAllocationDisallowed("ThreadLocalAllocation.slowPathNewInstanceWithoutAllocating", DynamicHub.toClass(hub).getName()); - GCImpl.getGCImpl().maybeCollectOnAllocation(); + GCImpl.getGCImpl().maybeCollectOnAllocation(size); AlignedHeader newTlab = HeapImpl.getChunkProvider().produceAlignedChunk(); - return allocateInstanceInNewTlab(hub, newTlab); + return allocateInstanceInNewTlab(hub, size, newTlab); } finally { - ObjectAllocationInNewTLABEvent.emit(startTicks, hub, LayoutEncoding.getPureInstanceAllocationSize(hub.getLayoutEncoding()), HeapParameters.getAlignedHeapChunkSize()); + ObjectAllocationInNewTLABEvent.emit(startTicks, hub, size, HeapParameters.getAlignedHeapChunkSize()); DeoptTester.enableDeoptTesting(); } } @@ -296,7 +297,7 @@ private static Object slowPathNewArrayLikeObject0(DynamicHub hub, int length, Un UnsignedWord tlabSize = HeapParameters.getAlignedHeapChunkSize(); try { HeapImpl.exitIfAllocationDisallowed("ThreadLocalAllocation.slowPathNewArrayOrPodWithoutAllocating", DynamicHub.toClass(hub).getName()); - GCImpl.getGCImpl().maybeCollectOnAllocation(); + GCImpl.getGCImpl().maybeCollectOnAllocation(size); if (size.aboveOrEqual(HeapParameters.getLargeArrayThreshold())) { /* Large arrays go into their own unaligned chunk. */ @@ -305,10 +306,12 @@ private static Object slowPathNewArrayLikeObject0(DynamicHub hub, int length, Un tlabSize = UnalignedHeapChunk.getChunkSizeForObject(size); return allocateLargeArrayLikeObjectInNewTlab(hub, length, size, newTlabChunk, needsZeroing, podReferenceMap); } - /* Small arrays go into the regular aligned chunk. */ - // We might have allocated in the caller and acquired a TLAB with enough space already - // (but we need to check in an uninterruptible method to be safe) + /* + * Small arrays go into the regular aligned chunk. We might have allocated in the caller + * and acquired a TLAB with enough space already (but we need to check in an + * uninterruptible method to be safe). + */ Object array = allocateSmallArrayLikeObjectInCurrentTlab(hub, length, size, podReferenceMap); if (array == null) { // We need a new chunk. AlignedHeader newTlabChunk = HeapImpl.getChunkProvider().produceAlignedChunk(); @@ -322,8 +325,8 @@ private static Object slowPathNewArrayLikeObject0(DynamicHub hub, int length, Un } @Uninterruptible(reason = "Holds uninitialized memory.") - private static Object allocateInstanceInNewTlab(DynamicHub hub, AlignedHeader newTlabChunk) { - UnsignedWord size = LayoutEncoding.getPureInstanceAllocationSize(hub.getLayoutEncoding()); + private static Object allocateInstanceInNewTlab(DynamicHub hub, UnsignedWord size, AlignedHeader newTlabChunk) { + assert size.equal(LayoutEncoding.getPureInstanceAllocationSize(hub.getLayoutEncoding())); Pointer memory = allocateRawMemoryInNewTlab(size, newTlabChunk); return FormatObjectNode.formatObject(memory, DynamicHub.toClass(hub), false, FillContent.WITH_ZEROES, true); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/GC.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/GC.java index 122c8f07e3e1..f2eed9860810 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/GC.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/GC.java @@ -31,13 +31,15 @@ public interface GC { /** Cause a full collection. */ void collectCompletely(GCCause cause); + /** + * Notify the GC that it might be a good time to do a collection. The final decision is up to + * the GC and its policy. + */ + void collectionHint(boolean fullGC); + /** Human-readable name. */ String getName(); /** Human-readable default heap size. */ String getDefaultMaxHeapSize(); - - /** Issue an optional GC request. */ - default void maybeCauseUserRequestedCollection(@SuppressWarnings("unused") GCCause cause, @SuppressWarnings("unused") boolean fullGC) { - } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java index 9edd9e57e45f..70a673e36ebc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java @@ -63,6 +63,8 @@ public final class JfrEvent { public static final JfrEvent ObjectAllocationInNewTLAB = create("jdk.ObjectAllocationInNewTLAB", false); public static final JfrEvent GCHeapSummary = create("jdk.GCHeapSummary", false); public static final JfrEvent ThreadAllocationStatistics = create("jdk.ThreadAllocationStatistics", false); + public static final JfrEvent SystemGC = create("jdk.SystemGC", true); + public static final JfrEvent AllocationRequiringGC = create("jdk.AllocationRequiringGC", false); private final long id; private final String name; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/AllocationRequiringGCEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/AllocationRequiringGCEvent.java new file mode 100644 index 000000000000..1357b40ae495 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/AllocationRequiringGCEvent.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, 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.jfr.events; + +import org.graalvm.nativeimage.StackValue; +import org.graalvm.word.UnsignedWord; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.jfr.HasJfrSupport; +import com.oracle.svm.core.jfr.JfrEvent; +import com.oracle.svm.core.jfr.JfrNativeEventWriter; +import com.oracle.svm.core.jfr.JfrNativeEventWriterData; +import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.SubstrateJVM; + +public class AllocationRequiringGCEvent { + public static void emit(UnsignedWord gcId, UnsignedWord size) { + if (HasJfrSupport.get()) { + emit0(gcId, size); + } + } + + @Uninterruptible(reason = "Accesses a JFR buffer.") + private static void emit0(UnsignedWord gcId, UnsignedWord size) { + if (JfrEvent.AllocationRequiringGC.shouldEmit()) { + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.AllocationRequiringGC); + JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); + JfrNativeEventWriter.putEventThread(data); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.AllocationRequiringGC, 0)); + JfrNativeEventWriter.putLong(data, gcId.rawValue()); + JfrNativeEventWriter.putLong(data, size.rawValue()); + JfrNativeEventWriter.endSmallEvent(data); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorWaitEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorWaitEvent.java index 00a66a16bd0d..b734bbf63a8e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorWaitEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/JavaMonitorWaitEvent.java @@ -50,6 +50,7 @@ private static void emit0(long startTicks, Object obj, long notifier, long timeo if (JfrEvent.JavaMonitorWait.shouldEmit(duration)) { JfrNativeEventWriterData data = org.graalvm.nativeimage.StackValue.get(JfrNativeEventWriterData.class); JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.JavaMonitorWait); JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putLong(data, duration); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ObjectAllocationInNewTLABEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ObjectAllocationInNewTLABEvent.java index 76226394d56a..dcccd614a7b4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ObjectAllocationInNewTLABEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ObjectAllocationInNewTLABEvent.java @@ -50,6 +50,7 @@ private static void emit0(long startTicks, DynamicHub hub, UnsignedWord allocati if (JfrEvent.ObjectAllocationInNewTLAB.shouldEmit()) { JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ObjectAllocationInNewTLAB); JfrNativeEventWriter.putLong(data, startTicks); JfrNativeEventWriter.putEventThread(data); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/SystemGCEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/SystemGCEvent.java new file mode 100644 index 000000000000..8123b992f3ca --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/SystemGCEvent.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, 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.jfr.events; + +import org.graalvm.nativeimage.StackValue; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.jfr.HasJfrSupport; +import com.oracle.svm.core.jfr.JfrEvent; +import com.oracle.svm.core.jfr.JfrNativeEventWriter; +import com.oracle.svm.core.jfr.JfrNativeEventWriterData; +import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.jfr.SubstrateJVM; + +public class SystemGCEvent { + public static void emit(long startTicks, boolean invokedConcurrent) { + if (HasJfrSupport.get()) { + emit0(startTicks, invokedConcurrent); + } + } + + @Uninterruptible(reason = "Accesses a JFR buffer.") + private static void emit0(long startTicks, boolean invokedConcurrent) { + long duration = JfrTicks.duration(startTicks); + if (JfrEvent.SystemGC.shouldEmit(duration)) { + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.SystemGC); + JfrNativeEventWriter.putLong(data, startTicks); + JfrNativeEventWriter.putLong(data, duration); + JfrNativeEventWriter.putEventThread(data); + JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.SystemGC, 0)); + JfrNativeEventWriter.putBoolean(data, invokedConcurrent); + JfrNativeEventWriter.endSmallEvent(data); + } + } +} diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java index 51d7df86f256..ec12d2082c07 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java @@ -124,7 +124,6 @@ import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.meta.RuntimeConfiguration; import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; -import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.log.FunctionPointerLogHandler; import com.oracle.svm.core.option.HostedOptionKey; @@ -743,7 +742,7 @@ private static void notifyLowMemoryPoint(boolean fullGC) { HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) HotSpotJVMCIRuntime.runtime().getCompiler(); // With Xcomp, do not force GCs as we don't care about RSS in this mode. if (!compiler.getGraalRuntime().getVMConfig().xcompMode) { - Heap.getHeap().getGC().maybeCauseUserRequestedCollection(GCCause.HintedGC, fullGC); + Heap.getHeap().getGC().collectionHint(fullGC); } } } diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/truffle/TruffleToLibGraalEntryPoints.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/truffle/TruffleToLibGraalEntryPoints.java index 01eb72ae783b..17b822f577ad 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/truffle/TruffleToLibGraalEntryPoints.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/truffle/TruffleToLibGraalEntryPoints.java @@ -76,7 +76,6 @@ import org.graalvm.word.PointerBase; import org.graalvm.word.WordFactory; -import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.graal.hotspot.libgraal.LibGraal; import com.oracle.svm.graal.hotspot.libgraal.LibGraalUtil; @@ -243,7 +242,7 @@ public static void doCompile(JNIEnv env, compiler.doCompile(task, compilable, listener); } finally { Heap.getHeap().doReferenceHandling(); - Heap.getHeap().getGC().maybeCauseUserRequestedCollection(GCCause.HintedGC, true); + Heap.getHeap().getGC().collectionHint(true); } } catch (Throwable t) { JNIExceptionWrapper.throwInHotSpot(env, t); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java new file mode 100644 index 000000000000..a3f675115d8d --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestAllocationRequiringGCEvent.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, 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.jfr; + +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; + +import com.oracle.svm.core.NeverInline; +import com.oracle.svm.core.jfr.JfrEvent; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; + +public class TestAllocationRequiringGCEvent extends JfrRecordingTest { + @Test + public void test() throws Throwable { + String[] events = new String[]{JfrEvent.AllocationRequiringGC.getName()}; + Recording recording = startRecording(events); + + /* Allocate 256 arrays with 1 MB each. */ + for (int i = 0; i < 256; i++) { + allocateByteArray(1024 * 1024); + } + + stopRecording(recording, TestAllocationRequiringGCEvent::validateEvents); + } + + private static void validateEvents(List events) { + assertTrue(events.size() > 0); + } + + @NeverInline("Prevent escape analysis.") + private static byte[] allocateByteArray(int length) { + return new byte[length]; + } +} diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java new file mode 100644 index 000000000000..655607f23bf7 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSystemGCEvent.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, 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.jfr; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; + +import com.oracle.svm.core.jfr.JfrEvent; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; + +public class TestSystemGCEvent extends JfrRecordingTest { + @Test + public void test() throws Throwable { + String[] events = new String[]{JfrEvent.SystemGC.getName()}; + Recording recording = startRecording(events); + + System.gc(); + + stopRecording(recording, TestSystemGCEvent::validateEvents); + } + + private static void validateEvents(List events) { + for (RecordedEvent event : events) { + assertNotNull(event.getStackTrace()); + assertNotNull(event.getThread()); + assertNotNull(event.getStartTime()); + assertTrue(event.getStartTime().toEpochMilli() <= System.currentTimeMillis()); + assertFalse(event.getBoolean("invokedConcurrent")); + } + } +}