From fe7d3dac421bfa5fb80de1210a13c954e2a90a35 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Fri, 7 Nov 2025 15:37:43 -0500 Subject: [PATCH 1/2] adjust global buffer defaults --- .../com/oracle/svm/core/jfr/JfrOptionSet.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrOptionSet.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrOptionSet.java index a4a73eb3eb24..881cdaa7861f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrOptionSet.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrOptionSet.java @@ -75,12 +75,28 @@ public JfrOptionSet() { } public void validateAndAdjustMemoryOptions() { + maybeAdjustDefaults(); ensureValidMinimumSizes(); ensureValidMemoryRelations(); adjustMemoryOptions(); assert checkPostCondition(); } + /** + * We expect that Native Image executables will require lesser defaults than JFR on the JVM. So + * to reduce JFR native memory consumption, we use lower default values. We should not adjust + * default values too early (at build time) because if any settings are user defined, we + * shouldn't change any values. + */ + private void maybeAdjustDefaults() { + if (!globalBufferSize.isUserValue() && !globalBufferCount.isUserValue() && !memorySize.isUserValue()) { + globalBufferSize.setValue(globalBufferSize.defaultValue / 2); + globalBufferCount.setValue(globalBufferCount.defaultValue / 2); + // Update record of total size + memorySize.setValue(globalBufferSize.getValue() * globalBufferCount.getValue()); + } + } + private void ensureValidMinimumSizes() { if (memorySize.isUserValue() && memorySize.getValue() < MIN_MEMORY_SIZE) { throw new IllegalStateException("The value specified for the JFR option 'memorysize' is too low. Please use at least " + MIN_MEMORY_SIZE + " bytes."); From 46fe33e359b8b70fd801713e8870d16925104bd8 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Mon, 10 Nov 2025 11:22:02 +0100 Subject: [PATCH 2/2] Set default size at build-time. Small cleanups. --- .../com/oracle/svm/core/jfr/JfrOptionSet.java | 92 +++++++++---------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrOptionSet.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrOptionSet.java index 881cdaa7861f..c5085bade2bd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrOptionSet.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrOptionSet.java @@ -28,9 +28,9 @@ import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.os.VirtualMemoryProvider; +import com.oracle.svm.core.util.BasedOnJDKFile; import jdk.graal.compiler.core.common.NumUtil; -import jdk.jfr.internal.Options; /** * Holds all JFR-related options that can be set by the user. It is also used to validate and adjust @@ -44,18 +44,32 @@ * level via {@link jdk.jfr.internal.JVM}. The option values are stored here until they are * eventually used when first recording is created and JFR is initialized. */ +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25-ga/src/jdk.jfr/share/classes/jdk/jfr/internal/Options.java#L48-L55") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25-ga/src/jdk.jfr/share/classes/jdk/jfr/internal/Options.java#L65-L69") public class JfrOptionSet { - private static final int MEMORY_SIZE = 1; - private static final int GLOBAL_BUFFER_SIZE = 2; - private static final int GLOBAL_BUFFER_COUNT = 4; - private static final int THREAD_BUFFER_SIZE = 8; + private static final int MEMORY_SIZE_BIT = 1; + private static final int GLOBAL_BUFFER_SIZE_BIT = 2; + private static final int GLOBAL_BUFFER_COUNT_BIT = 4; + private static final int THREAD_BUFFER_SIZE_BIT = 8; + + /* + * Use 2.5 MB by default (instead of 10 MB on HotSpot). We explicitly hard-code the default + * values here to avoid that those values are copied from HotSpot at build-time because + * completely different values might be set there if JFR is enabled at build-time as well. + */ + private static final long DEFAULT_GLOBAL_BUFFER_COUNT = 10; + private static final long DEFAULT_GLOBAL_BUFFER_SIZE = 256 * 1024; + private static final long DEFAULT_MEMORY_SIZE = DEFAULT_GLOBAL_BUFFER_COUNT * DEFAULT_GLOBAL_BUFFER_SIZE; + private static final long DEFAULT_THREAD_BUFFER_SIZE = 8 * 1024; + private static final int DEFAULT_STACK_DEPTH = 64; + private static final long DEFAULT_MAX_CHUNK_SIZE = 12 * 1024 * 1024; private static final long MAX_ADJUSTED_GLOBAL_BUFFER_SIZE = 1 * 1024 * 1024; private static final long MIN_ADJUSTED_GLOBAL_BUFFER_SIZE_CUTOFF = 512 * 1024; private static final long MIN_GLOBAL_BUFFER_SIZE = 64 * 1024; private static final long MIN_GLOBAL_BUFFER_COUNT = 2; private static final long MIN_THREAD_BUFFER_SIZE = 4 * 1024; - private static final long MIN_MEMORY_SIZE = 1 * 1024 * 1024; + private static final long MIN_MEMORY_SIZE = 1024 * 1024; public final JfrOptionLong threadBufferSize; public final JfrOptionLong globalBufferSize; @@ -66,37 +80,21 @@ public class JfrOptionSet { @Platforms(Platform.HOSTED_ONLY.class) public JfrOptionSet() { - threadBufferSize = new JfrOptionLong(Options.getThreadBufferSize()); - globalBufferSize = new JfrOptionLong(Options.getGlobalBufferSize()); - globalBufferCount = new JfrOptionLong(Options.getGlobalBufferCount()); - memorySize = new JfrOptionLong(Options.getMemorySize()); - maxChunkSize = new JfrOptionLong(Options.getMaxChunkSize()); - stackDepth = new JfrOptionLong(Options.getStackDepth()); + threadBufferSize = new JfrOptionLong(DEFAULT_THREAD_BUFFER_SIZE); + globalBufferSize = new JfrOptionLong(DEFAULT_GLOBAL_BUFFER_SIZE); + globalBufferCount = new JfrOptionLong(DEFAULT_GLOBAL_BUFFER_COUNT); + memorySize = new JfrOptionLong(DEFAULT_MEMORY_SIZE); + maxChunkSize = new JfrOptionLong(DEFAULT_MAX_CHUNK_SIZE); + stackDepth = new JfrOptionLong(DEFAULT_STACK_DEPTH); } public void validateAndAdjustMemoryOptions() { - maybeAdjustDefaults(); ensureValidMinimumSizes(); ensureValidMemoryRelations(); adjustMemoryOptions(); assert checkPostCondition(); } - /** - * We expect that Native Image executables will require lesser defaults than JFR on the JVM. So - * to reduce JFR native memory consumption, we use lower default values. We should not adjust - * default values too early (at build time) because if any settings are user defined, we - * shouldn't change any values. - */ - private void maybeAdjustDefaults() { - if (!globalBufferSize.isUserValue() && !globalBufferCount.isUserValue() && !memorySize.isUserValue()) { - globalBufferSize.setValue(globalBufferSize.defaultValue / 2); - globalBufferCount.setValue(globalBufferCount.defaultValue / 2); - // Update record of total size - memorySize.setValue(globalBufferSize.getValue() * globalBufferCount.getValue()); - } - } - private void ensureValidMinimumSizes() { if (memorySize.isUserValue() && memorySize.getValue() < MIN_MEMORY_SIZE) { throw new IllegalStateException("The value specified for the JFR option 'memorysize' is too low. Please use at least " + MIN_MEMORY_SIZE + " bytes."); @@ -136,55 +134,55 @@ private void adjustMemoryOptions() { int setOfOptions = 0; if (memorySize.isUserValue()) { - setOfOptions |= MEMORY_SIZE; + setOfOptions |= MEMORY_SIZE_BIT; } if (globalBufferSize.isUserValue()) { - setOfOptions |= GLOBAL_BUFFER_SIZE; + setOfOptions |= GLOBAL_BUFFER_SIZE_BIT; } if (globalBufferCount.isUserValue()) { - setOfOptions |= GLOBAL_BUFFER_COUNT; + setOfOptions |= GLOBAL_BUFFER_COUNT_BIT; } if (threadBufferSize.isUserValue()) { - setOfOptions |= THREAD_BUFFER_SIZE; + setOfOptions |= THREAD_BUFFER_SIZE_BIT; } switch (setOfOptions) { - case MEMORY_SIZE | THREAD_BUFFER_SIZE: - case MEMORY_SIZE: + case MEMORY_SIZE_BIT | THREAD_BUFFER_SIZE_BIT: + case MEMORY_SIZE_BIT: memoryAndThreadBufferSize(); break; - case MEMORY_SIZE | GLOBAL_BUFFER_COUNT: + case MEMORY_SIZE_BIT | GLOBAL_BUFFER_COUNT_BIT: memorySizeAndBufferCount(); break; - case MEMORY_SIZE | GLOBAL_BUFFER_SIZE | THREAD_BUFFER_SIZE: + case MEMORY_SIZE_BIT | GLOBAL_BUFFER_SIZE_BIT | THREAD_BUFFER_SIZE_BIT: assert threadBufferSize.isUserValue(); // fall through - case MEMORY_SIZE | GLOBAL_BUFFER_SIZE: + case MEMORY_SIZE_BIT | GLOBAL_BUFFER_SIZE_BIT: memorySizeAndGlobalBufferSize(); break; - case MEMORY_SIZE | GLOBAL_BUFFER_SIZE | GLOBAL_BUFFER_COUNT: - case MEMORY_SIZE | GLOBAL_BUFFER_SIZE | GLOBAL_BUFFER_COUNT | THREAD_BUFFER_SIZE: + case MEMORY_SIZE_BIT | GLOBAL_BUFFER_SIZE_BIT | GLOBAL_BUFFER_COUNT_BIT: + case MEMORY_SIZE_BIT | GLOBAL_BUFFER_SIZE_BIT | GLOBAL_BUFFER_COUNT_BIT | THREAD_BUFFER_SIZE_BIT: allOptionsSet(); break; - case GLOBAL_BUFFER_SIZE | GLOBAL_BUFFER_COUNT | THREAD_BUFFER_SIZE: + case GLOBAL_BUFFER_SIZE_BIT | GLOBAL_BUFFER_COUNT_BIT | THREAD_BUFFER_SIZE_BIT: assert globalBufferCount.isUserValue(); assert threadBufferSize.isUserValue(); // fall through - case GLOBAL_BUFFER_SIZE | GLOBAL_BUFFER_COUNT: + case GLOBAL_BUFFER_SIZE_BIT | GLOBAL_BUFFER_COUNT_BIT: assert globalBufferSize.isUserValue(); // fall through - case GLOBAL_BUFFER_SIZE | THREAD_BUFFER_SIZE: - case GLOBAL_BUFFER_COUNT: - case GLOBAL_BUFFER_SIZE: + case GLOBAL_BUFFER_SIZE_BIT | THREAD_BUFFER_SIZE_BIT: + case GLOBAL_BUFFER_COUNT_BIT: + case GLOBAL_BUFFER_SIZE_BIT: globalBufferSize(); break; - case MEMORY_SIZE | GLOBAL_BUFFER_COUNT | THREAD_BUFFER_SIZE: + case MEMORY_SIZE_BIT | GLOBAL_BUFFER_COUNT_BIT | THREAD_BUFFER_SIZE_BIT: assert memorySize.isUserValue(); // fall through - case GLOBAL_BUFFER_COUNT | THREAD_BUFFER_SIZE: + case GLOBAL_BUFFER_COUNT_BIT | THREAD_BUFFER_SIZE_BIT: assert globalBufferCount.isUserValue(); // fall through - case THREAD_BUFFER_SIZE: + case THREAD_BUFFER_SIZE_BIT: threadBufferSize(); break; default: