From 624ff2fb6e899ff9198cfe27b3994a1a2f50e90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarren=CC=83o?= Date: Tue, 18 Jul 2023 14:52:37 +0200 Subject: [PATCH 1/4] Add support for -XX:+HeapDumpOnOutOfMemoryError --- .../org/graalvm/nativeimage/VMRuntime.java | 14 ++++++ .../nativeimage/impl/HeapDumpSupport.java | 3 ++ .../oracle/svm/core/genscavenge/GCImpl.java | 12 ++++- .../posix/PosixRawFileOperationSupport.java | 18 +++++++ .../svm/core/DumpHeapOnSignalFeature.java | 5 ++ .../com/oracle/svm/core/SubstrateOptions.java | 7 +++ .../core/heap/dump/HeapDumpSupportImpl.java | 50 +++++++++++++++++-- .../core/heapdump/HeapDumpSupportImpl.java | 10 ++++ .../svm/core/os/RawFileOperationSupport.java | 18 +++++++ 9 files changed, 131 insertions(+), 6 deletions(-) diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/VMRuntime.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/VMRuntime.java index 983207c0dbcc..a367548bc523 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/VMRuntime.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/VMRuntime.java @@ -92,6 +92,20 @@ public static void dumpHeap(String outputFile, boolean live) throws IOException ImageSingletons.lookup(HeapDumpSupport.class).dumpHeap(outputFile, live); } + /** + * Dumps the heap to a predetermined file in case of an @{@link OutOfMemoryError}, in the same format as the hprof heap dump. + * + * @throws UnsupportedOperationException if this operation is not supported. + * + * @since 23.1 + */ + public static void dumpHeapOnOutOfMemoryError() { + if (!ImageSingletons.contains(HeapDumpSupport.class)) { + throw new UnsupportedOperationException(); + } + ImageSingletons.lookup(HeapDumpSupport.class).dumpHeapOnOutOfMemoryError(); + } + private VMRuntime() { } } diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/HeapDumpSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/HeapDumpSupport.java index 444974571abc..da8180d0be71 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/HeapDumpSupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/HeapDumpSupport.java @@ -44,4 +44,7 @@ public interface HeapDumpSupport { void dumpHeap(String outputFile, boolean live) throws java.io.IOException; + void dumpHeapOnOutOfMemoryError(); + + void initHeapDumpOnOutOfMemoryErrorPath(); } 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 65415bf20647..2b0d98e96e0c 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 @@ -35,6 +35,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.VMRuntime; import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.nativeimage.c.struct.RawField; import org.graalvm.nativeimage.c.struct.RawStructure; @@ -150,10 +151,17 @@ public void maybeCollectOnAllocation() { outOfMemory = collectWithoutAllocating(GenScavengeGCCause.OnAllocation, false); } if (outOfMemory) { - throw OutOfMemoryUtil.heapSizeExceeded(); + heapSizeExceeded(); } } + private static void heapSizeExceeded() { + if (SubstrateOptions.isHeapDumpOnOutOfMemoryError()) { + VMRuntime.dumpHeapOnOutOfMemoryError(); + } + throw OutOfMemoryUtil.heapSizeExceeded(); + } + @Override public void maybeCauseUserRequestedCollection(GCCause cause, boolean fullGC) { if (policy.shouldCollectOnRequest(cause, fullGC)) { @@ -165,7 +173,7 @@ private void collect(GCCause cause, boolean forceFullGC) { if (!hasNeverCollectPolicy()) { boolean outOfMemory = collectWithoutAllocating(cause, forceFullGC); if (outOfMemory) { - throw OutOfMemoryUtil.heapSizeExceeded(); + heapSizeExceeded(); } } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixRawFileOperationSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixRawFileOperationSupport.java index a17c5b5b09ec..c44c3942f995 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixRawFileOperationSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixRawFileOperationSupport.java @@ -30,6 +30,7 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.word.Pointer; import org.graalvm.word.SignedWord; @@ -60,6 +61,17 @@ public RawFileDescriptor create(File file, FileCreationMode creationMode, FileAc return open0(path, flags); } + @Override + public RawFileDescriptor create(CCharPointer cPath, FileCreationMode creationMode, FileAccessMode accessMode) { + int flags = parseMode(creationMode) | parseMode(accessMode); + return open0(cPath, flags); + } + + private static RawFileDescriptor open0(CCharPointer cPath, int flags) { + int permissions = PosixStat.S_IRUSR() | PosixStat.S_IWUSR(); + return WordFactory.signed(Fcntl.NoTransitions.open(cPath, flags, permissions)); + } + @Override public RawFileDescriptor open(File file, FileAccessMode mode) { String path = file.getPath(); @@ -67,6 +79,12 @@ public RawFileDescriptor open(File file, FileAccessMode mode) { return open0(path, flags); } + @Override + public RawFileDescriptor open(CCharPointer cPath, FileAccessMode mode) { + int flags = parseMode(mode); + return open0(cPath, flags); + } + private static RawFileDescriptor open0(String path, int flags) { int permissions = PosixStat.S_IRUSR() | PosixStat.S_IWUSR(); try (CTypeConversion.CCharPointerHolder cPath = CTypeConversion.toCString(path)) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpHeapOnSignalFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpHeapOnSignalFeature.java index 212ae60d9c14..c5c1f6dcc512 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpHeapOnSignalFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DumpHeapOnSignalFeature.java @@ -30,6 +30,7 @@ import java.util.Date; import java.util.TimeZone; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.ProcessProperties; import org.graalvm.nativeimage.VMRuntime; @@ -39,6 +40,7 @@ import com.oracle.svm.core.log.Log; import jdk.internal.misc.Signal; +import org.graalvm.nativeimage.impl.HeapDumpSupport; @AutomaticallyRegisteredFeature public class DumpHeapOnSignalFeature implements InternalFeature { @@ -59,6 +61,9 @@ final class DumpHeapStartupHook implements RuntimeSupport.Hook { public void execute(boolean isFirstIsolate) { if (isFirstIsolate && SubstrateOptions.EnableSignalHandling.getValue()) { DumpHeapReport.install(); + if (SubstrateOptions.isHeapDumpOnOutOfMemoryError()) { + ImageSingletons.lookup(HeapDumpSupport.class).initHeapDumpOnOutOfMemoryErrorPath(); + } } } } 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 23e0de2ea169..0d11295f5712 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 @@ -825,6 +825,13 @@ public Boolean getValue(OptionValues values) { } }; + @Option(help = "Dump heap to file when java.lang.OutOfMemoryError is thrown.")// + public static final RuntimeOptionKey HeapDumpOnOutOfMemoryError = new RuntimeOptionKey<>(false, Immutable); + + public static boolean isHeapDumpOnOutOfMemoryError() { + return VMInspectionOptions.hasHeapDumpSupport() && SubstrateOptions.HeapDumpOnOutOfMemoryError.getValue(); + } + @Option(help = "The path (filename or directory) where heap dumps are created (defaults to the working directory).")// public static final RuntimeOptionKey HeapDumpPath = new RuntimeOptionKey<>("", Immutable); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java index 7641b3e75f68..a434b2a10219 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java @@ -25,19 +25,26 @@ package com.oracle.svm.core.heap.dump; import static com.oracle.svm.core.heap.RestrictHeapAccess.Access.NO_ALLOCATION; -import static com.oracle.svm.core.heap.RestrictHeapAccess.Access.UNRESTRICTED; import java.io.IOException; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.headers.LibC; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.ProcessProperties; import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.struct.RawField; import org.graalvm.nativeimage.c.struct.RawStructure; import org.graalvm.nativeimage.c.struct.SizeOf; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.impl.HeapDumpSupport; import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; import com.oracle.svm.core.UnmanagedMemoryUtil; @@ -56,6 +63,7 @@ public class HeapDumpSupportImpl implements HeapDumpSupport { private final HeapDumpWriter writer; private final HeapDumpOperation heapDumpOperation; + private CCharPointer heapOnErrorDumpPath; @Platforms(Platform.HOSTED_ONLY.class) public HeapDumpSupportImpl(HeapDumpMetadata metadata) { @@ -77,6 +85,40 @@ public void dumpHeap(String filename, boolean gcBefore) throws IOException { } } + @Override + @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Heap dumping on OutOfMemoryError must not allocate.") + public void dumpHeapOnOutOfMemoryError() { + final RawFileDescriptor fd = getFileSupport().create(heapOnErrorDumpPath, FileCreationMode.CREATE_OR_REPLACE, RawFileOperationSupport.FileAccessMode.READ_WRITE); + if (!getFileSupport().isValid(fd)) { + Log.log().string("Invalid file descriptor opening heap dump on OutOfMemoryError.").newline(); + return; + } + + int size = SizeOf.get(HeapDumpVMOperationData.class); + HeapDumpVMOperationData data = StackValue.get(size); + UnmanagedMemoryUtil.fill((Pointer) data, WordFactory.unsigned(size), (byte) 0); + + data.setGCBefore(false); + data.setRawFileDescriptor(fd); + heapDumpOperation.enqueue(data); + } + + @Override + public void initHeapDumpOnOutOfMemoryErrorPath() { + String dumpFileName = "svm-heapdump-" + ProcessProperties.getProcessID() + ".hprof"; + String dumpPath = SubstrateOptions.getHeapDumpPath(dumpFileName); + try (CTypeConversion.CCharPointerHolder cPath = CTypeConversion.toCString(dumpPath)) { + heapOnErrorDumpPath = copyDumpPath(cPath.get()); + } + } + + private static CCharPointer copyDumpPath(CCharPointer cPath) { + final UnsignedWord length = SubstrateUtil.strlen(cPath); + final CCharPointer copy = UnmanagedMemory.malloc(length); + LibC.memcpy(copy, cPath, length); + return copy; + } + public void writeHeapTo(RawFileDescriptor fd, boolean gcBefore) throws IOException { int size = SizeOf.get(HeapDumpVMOperationData.class); HeapDumpVMOperationData data = StackValue.get(size); @@ -136,13 +178,13 @@ protected void operate(NativeVMOperationData d) { data.setSuccess(success); } catch (Throwable e) { reportError(e); + data.setSuccess(false); } } - @RestrictHeapAccess(access = UNRESTRICTED, reason = "Error reporting may allocate.") + @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Error reporting must not allocate.") private static void reportError(Throwable e) { - Log.log().string("An exception occurred during heap dumping. The data in the heap dump file may be corrupt.").newline().string(e.getClass().getName()).string(": ") - .string(e.getMessage()); + Log.log().string("An exception occurred during heap dumping. The data in the heap dump file may be corrupt: ").string(e.getClass().getName()); } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heapdump/HeapDumpSupportImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heapdump/HeapDumpSupportImpl.java index 1ac7046f8550..ad5574b92b38 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heapdump/HeapDumpSupportImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heapdump/HeapDumpSupportImpl.java @@ -36,4 +36,14 @@ public void dumpHeap(String outputFile, boolean live) throws java.io.IOException com.oracle.svm.core.heapdump.HeapDumpWriter.singleton().writeHeapTo(fileOutputStream, live); } } + + @Override + public void dumpHeapOnOutOfMemoryError() { + // No-op + } + + @Override + public void initHeapDumpOnOutOfMemoryErrorPath() { + // No-op + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/RawFileOperationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/RawFileOperationSupport.java index b0d4aa0b720d..4404be3ac985 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/RawFileOperationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/RawFileOperationSupport.java @@ -28,6 +28,7 @@ import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordBase; @@ -88,6 +89,15 @@ static RawFileOperationSupport nativeByteOrder() { */ RawFileDescriptor create(File file, FileCreationMode creationMode, FileAccessMode accessMode); + /** + * Creates a file with the specified {@link FileCreationMode creation} and {@link FileAccessMode + * access modes}. + * + * @return If the operation is successful, it returns the file descriptor. Otherwise, it returns + * a value where {@link #isValid} will return false. + */ + RawFileDescriptor create(CCharPointer path, FileCreationMode creationMode, FileAccessMode accessMode); + /** * Opens a file with the specified {@link FileAccessMode access mode}. * @@ -104,6 +114,14 @@ static RawFileOperationSupport nativeByteOrder() { */ RawFileDescriptor open(File file, FileAccessMode accessMode); + /** + * Opens a file with the specified {@link FileAccessMode access mode}. + * + * @return If the operation is successful, it returns the file descriptor. Otherwise, it returns + * a value where {@link #isValid} will return false. + */ + RawFileDescriptor open(CCharPointer path, FileAccessMode accessMode); + /** * Checks if a file descriptor is valid or if it represents an error value. * From f75d2e2e13a9a83b823a264e212d20e7db69d059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarren=CC=83o?= Date: Tue, 18 Jul 2023 15:45:27 +0200 Subject: [PATCH 2/4] Log when heap dump is generated on OutOfMemoryError --- .../com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java index a434b2a10219..44e004ef54b6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java @@ -88,9 +88,11 @@ public void dumpHeap(String filename, boolean gcBefore) throws IOException { @Override @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Heap dumping on OutOfMemoryError must not allocate.") public void dumpHeapOnOutOfMemoryError() { + final Log log = Log.log(); + final RawFileDescriptor fd = getFileSupport().create(heapOnErrorDumpPath, FileCreationMode.CREATE_OR_REPLACE, RawFileOperationSupport.FileAccessMode.READ_WRITE); if (!getFileSupport().isValid(fd)) { - Log.log().string("Invalid file descriptor opening heap dump on OutOfMemoryError.").newline(); + log.string("Invalid file descriptor opening heap dump on OutOfMemoryError.").newline(); return; } @@ -98,6 +100,8 @@ public void dumpHeapOnOutOfMemoryError() { HeapDumpVMOperationData data = StackValue.get(size); UnmanagedMemoryUtil.fill((Pointer) data, WordFactory.unsigned(size), (byte) 0); + log.string("Dumping heap to ").string(heapOnErrorDumpPath).string(" ...").newline(); + data.setGCBefore(false); data.setRawFileDescriptor(fd); heapDumpOperation.enqueue(data); From 37319360e42d4c9c4bd363b34bd326b82e2119e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarren=CC=83o?= Date: Tue, 18 Jul 2023 15:45:53 +0200 Subject: [PATCH 3/4] Add documentation for -XX:+HeapDumpOnOutOfMemoryError --- .../create-heap-dump-from-native-executable.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md b/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md index 97be87ecafbc..d53b5a21b3d0 100644 --- a/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md +++ b/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md @@ -9,12 +9,13 @@ permalink: /reference-manual/native-image/guides/create-heap-dump/ You can create a heap dump of a running executable to monitor its execution. Just like any other Java heap dump, it can be opened with the [VisualVM](../../../tools/visualvm.md) tool. -To enable heap dump support, native executables must be built with the `--enable-monitoring=heapdump` option. Heap dumps can then be created in three different ways: +To enable heap dump support, native executables must be built with the `--enable-monitoring=heapdump` option. Heap dumps can then be created in different ways: 1. Create heap dumps with VisualVM. 2. Dump the initial heap of a native executable using the `-XX:+DumpHeapAndExit` command-line option. 3. Create heap dumps sending a `SIGUSR1` signal at run time. 4. Create heap dumps programmatically using the [`org.graalvm.nativeimage.VMRuntime#dumpHeap`](https://github.com/oracle/graal/blob/master/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java) API. +5. If `-XX:+HeapDumpOnOutOfMemoryError` is passed in at runtime and an `OutOfMemoryError` is encountered, a heap dump will be produced. All approaches are described below. @@ -275,6 +276,18 @@ The condition to create a heap dump is provided as an option on the command line ![Native Image Heap Dump View in VisualVM](img/heap-dump-api.png) +## Create a Heap Dump on `OutOfMemoryError` + +Start the application with `-XX:+HeapDumpOnOutOfMemoryError` option to get a heap dump when an `OutOfMemoryError` occurs. +The generated heap dump file name will have the `svm-heapdump-.hprof` naming format. +For example: + +```shell +./example -XX:+HeapDumpOnOutOfMemoryError +Dumping heap to svm-heapdump-67799.hprof ... +Exception in thread "main" java.lang.OutOfMemoryError: Garbage-collected heap size exceeded. +``` + ### Related Documentation * [Debugging and Diagnostics](../DebuggingAndDiagnostics.md) From 9a8c441ba06ccc346b49c6d5402be3e752f4efac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarren=CC=83o?= Date: Wed, 26 Jul 2023 16:12:59 +0100 Subject: [PATCH 4/4] Generate only one heap dump on OOME --- .../com/oracle/svm/core/genscavenge/GCImpl.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 2b0d98e96e0c..f3051993631e 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 @@ -29,6 +29,7 @@ import java.lang.ref.Reference; +import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicBoolean; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.IsolateThread; @@ -117,6 +118,8 @@ public final class GCImpl implements GC { private UnsignedWord collectionEpoch = WordFactory.zero(); private long lastWholeHeapExaminedTimeMillis = -1; + private final AtomicBoolean outOfMemoryReported = new AtomicBoolean(false); + @Platforms(Platform.HOSTED_ONLY.class) GCImpl() { this.policy = CollectionPolicy.getInitialPolicy(); @@ -151,15 +154,15 @@ public void maybeCollectOnAllocation() { outOfMemory = collectWithoutAllocating(GenScavengeGCCause.OnAllocation, false); } if (outOfMemory) { - heapSizeExceeded(); + reportJavaOutOfMemory(); + throw OutOfMemoryUtil.heapSizeExceeded(); } } - private static void heapSizeExceeded() { - if (SubstrateOptions.isHeapDumpOnOutOfMemoryError()) { + private void reportJavaOutOfMemory() { + if (SubstrateOptions.isHeapDumpOnOutOfMemoryError() && outOfMemoryReported.compareAndSet(false, true)) { VMRuntime.dumpHeapOnOutOfMemoryError(); } - throw OutOfMemoryUtil.heapSizeExceeded(); } @Override @@ -173,7 +176,8 @@ private void collect(GCCause cause, boolean forceFullGC) { if (!hasNeverCollectPolicy()) { boolean outOfMemory = collectWithoutAllocating(cause, forceFullGC); if (outOfMemory) { - heapSizeExceeded(); + reportJavaOutOfMemory(); + throw OutOfMemoryUtil.heapSizeExceeded(); } } }