From a6dd2277115b40e21f7151c987f3320c9bc65637 Mon Sep 17 00:00:00 2001 From: Josef Eisl Date: Fri, 9 Aug 2024 12:14:38 +0200 Subject: [PATCH 1/3] svm: libcontainer: document functional change in CachedMetric [GR-55236] --- .../src/com.oracle.svm.native.libcontainer/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/README.md b/substratevm/src/com.oracle.svm.native.libcontainer/README.md index dc014ad7af60..ba58796ac1f6 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/README.md +++ b/substratevm/src/com.oracle.svm.native.libcontainer/README.md @@ -25,6 +25,14 @@ mostly unmodified except for sections guarded with `#ifdef NATIVE_IMAGE`. The Fi [`src/svm`](./src/svm) are replacements for files that exist in the OpenJDK, but are completely custom. They only provide the minimal required functionality and are specific to SVM. +## Deviation from Hotspot behavior + +On Hotspot, some of the values are cached in native code. Since native caching is undesired +on SVM, we disable it by always returning `true` in `CachedMetric::should_check_metric()`. +To avoid performance penalties compared to Hotspot, SVM caches those values on the Java side. +Currently, this only applies to `cpu_limit` and `memory_limit`. For every update of the +`libsvm_container` code, we should review whether new usages of `CachedMetric` were added. + ## Updating While the code in `libsvm_container` is completely independent and does not need to be in sync with From 89837ba44e2c49e9068a5c56edb8167def2b4e52 Mon Sep 17 00:00:00 2001 From: Josef Eisl Date: Wed, 7 Aug 2024 11:10:26 +0200 Subject: [PATCH 2/3] svm: move PhysicalMemory#getUsedSizeFromProcMemInfo to LinuxPhysicalMemorySupportImpl --- .../linux/LinuxPhysicalMemorySupportImpl.java | 72 ++++++++++++++++- .../oracle/svm/core/heap/PhysicalMemory.java | 77 ++++--------------- 2 files changed, 85 insertions(+), 64 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemorySupportImpl.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemorySupportImpl.java index f7e563b32d8f..ba1887f53c65 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemorySupportImpl.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemorySupportImpl.java @@ -24,17 +24,28 @@ */ package com.oracle.svm.core.posix.linux; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.container.Container; +import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; +import com.oracle.svm.core.heap.PhysicalMemory; import com.oracle.svm.core.heap.PhysicalMemory.PhysicalMemorySupport; import com.oracle.svm.core.posix.headers.Unistd; -import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.util.VMError; @AutomaticallyRegisteredImageSingleton(PhysicalMemorySupport.class) class LinuxPhysicalMemorySupportImpl implements PhysicalMemorySupport { + private static final long K = 1024; + @Override public UnsignedWord size() { long numberOfPhysicalMemoryPages = Unistd.sysconf(Unistd._SC_PHYS_PAGES()); @@ -44,4 +55,63 @@ public UnsignedWord size() { } return WordFactory.unsigned(numberOfPhysicalMemoryPages).multiply(WordFactory.unsigned(sizeOfAPhysicalMemoryPage)); } + + @Override + public long usedSize() { + /* + * Note: we use getCachedMemoryLimitInBytes() because we don't want to mutate the state, and + * we assume that the memory limits have be queried before calling this method. + */ + assert !(Container.singleton().isContainerized() && Container.singleton().getCachedMemoryLimitInBytes() > 0) : "Should be using OperatingSystemMXBean"; + /* Non-containerized Linux uses /proc/meminfo. */ + return getUsedSizeFromProcMemInfo(); + } + + private static long getUsedSizeFromProcMemInfo() { + try { + List lines = readAllLines("/proc/meminfo"); + for (String line : lines) { + if (line.contains("MemAvailable")) { + return PhysicalMemory.getCachedSize().rawValue() - parseFirstNumber(line) * K; + } + } + } catch (Exception e) { + /* Nothing to do. */ + } + return -1L; + } + + private static List readAllLines(String fileName) throws IOException { + List lines = new ArrayList<>(); + try (BufferedReader bufferedReader = new BufferedReader(new FileReader(fileName, StandardCharsets.UTF_8))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + lines.add(line); + } + } + return lines; + } + + /** Parses the first number in the String as a long value. */ + private static long parseFirstNumber(String str) { + int firstDigit = -1; + int lastDigit = -1; + + for (int i = 0; i < str.length(); i++) { + if (Character.isDigit(str.charAt(i))) { + if (firstDigit == -1) { + firstDigit = i; + } + lastDigit = i; + } else if (firstDigit != -1) { + break; + } + } + + if (firstDigit >= 0) { + String number = str.substring(firstDigit, lastDigit + 1); + return Long.parseLong(number); + } + return -1; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java index 020fb0b35d67..a6aaeff5ea2a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java @@ -24,14 +24,9 @@ */ package com.oracle.svm.core.heap; -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; import java.lang.management.ManagementFactory; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -54,9 +49,19 @@ public class PhysicalMemory { public interface PhysicalMemorySupport extends RuntimeOnlyImageSingleton { /** Get the size of physical memory from the OS. */ UnsignedWord size(); - } - private static final long K = 1024; + /** + * Returns the amount of used physical memory in bytes, or -1 if not supported. + * + * This is used as a fallback in case {@link java.lang.management.OperatingSystemMXBean} + * cannot be used. + * + * @see PhysicalMemory#usedSize() + */ + default long usedSize() { + return -1L; + } + } private static final UnsignedWord UNSET_SENTINEL = UnsignedUtils.MAX_VALUE; private static UnsignedWord cachedSize = UNSET_SENTINEL; @@ -104,61 +109,7 @@ public static long usedSize() { return osBean.getTotalMemorySize() - osBean.getFreeMemorySize(); } - // Non-containerized Linux uses /proc/meminfo. - if (Platform.includedIn(Platform.LINUX.class)) { - return getUsedSizeFromProcMemInfo(); - } - - return -1L; - } - - // Will be removed as part of GR-51479. - private static long getUsedSizeFromProcMemInfo() { - try { - List lines = readAllLines("/proc/meminfo"); - for (String line : lines) { - if (line.contains("MemAvailable")) { - return size().rawValue() - parseFirstNumber(line) * K; - } - } - } catch (Exception e) { - /* Nothing to do. */ - } - return -1L; - } - - private static List readAllLines(String fileName) throws IOException { - List lines = new ArrayList<>(); - try (BufferedReader bufferedReader = new BufferedReader(new FileReader(fileName, StandardCharsets.UTF_8))) { - String line; - while ((line = bufferedReader.readLine()) != null) { - lines.add(line); - } - } - return lines; - } - - /** Parses the first number in the String as a long value. */ - private static long parseFirstNumber(String str) { - int firstDigit = -1; - int lastDigit = -1; - - for (int i = 0; i < str.length(); i++) { - if (Character.isDigit(str.charAt(i))) { - if (firstDigit == -1) { - firstDigit = i; - } - lastDigit = i; - } else if (firstDigit != -1) { - break; - } - } - - if (firstDigit >= 0) { - String number = str.substring(firstDigit, lastDigit + 1); - return Long.parseLong(number); - } - return -1; + return ImageSingletons.lookup(PhysicalMemory.PhysicalMemorySupport.class).usedSize(); } /** From 6a261679036cf6c3ab6255376ce5827cf4536d4f Mon Sep 17 00:00:00 2001 From: Josef Eisl Date: Tue, 6 Aug 2024 13:46:02 +0200 Subject: [PATCH 3/3] svm: initialize libsvm_container eagerly --- .../genscavenge/AbstractCollectionPolicy.java | 9 +++--- .../genscavenge/BasicCollectionPolicies.java | 14 ++++----- .../oracle/svm/core/genscavenge/GCImpl.java | 6 +--- .../linux/LinuxPhysicalMemorySupportImpl.java | 2 +- .../svm/core/IsolateArgumentParser.java | 2 +- .../oracle/svm/core/SubstrateDiagnostics.java | 15 ++------- .../oracle/svm/core/container/Container.java | 31 +++++++------------ .../graal/snippets/CEntryPointSnippets.java | 11 +++++-- .../oracle/svm/core/heap/PhysicalMemory.java | 18 +++++------ .../EveryChunkNativePeriodicEvents.java | 2 +- 10 files changed, 44 insertions(+), 66 deletions(-) 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 5648d997142f..0602142883fd 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 @@ -26,8 +26,6 @@ import java.util.concurrent.atomic.AtomicBoolean; -import jdk.graal.compiler.api.replacements.Fold; -import jdk.graal.compiler.nodes.PauseNode; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.UnsignedWord; @@ -42,6 +40,9 @@ import com.oracle.svm.core.util.UnsignedUtils; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.nodes.PauseNode; + abstract class AbstractCollectionPolicy implements CollectionPolicy { protected static final int MIN_SPACE_SIZE_IN_ALIGNED_CHUNKS = 8; @@ -326,10 +327,8 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) { long optionMax = SubstrateGCOptions.MaxHeapSize.getValue(); if (optionMax > 0L) { maxHeap = WordFactory.unsigned(optionMax); - } else if (!PhysicalMemory.isInitialized()) { - maxHeap = addressSpaceSize; } else { - maxHeap = PhysicalMemory.getCachedSize().unsignedDivide(100).multiply(HeapParameters.getMaximumHeapSizePercent()); + maxHeap = PhysicalMemory.size().unsignedDivide(100).multiply(HeapParameters.getMaximumHeapSizePercent()); } UnsignedWord unadjustedMaxHeap = maxHeap; maxHeap = UnsignedUtils.clamp(alignDown(maxHeap), minAllSpaces, alignDown(addressSpaceSize)); 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 1de09da02942..5bf00a10b5ff 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 @@ -105,14 +105,12 @@ public final UnsignedWord getMaximumHeapSize() { * size of the physical memory. */ UnsignedWord addressSpaceSize = ReferenceAccess.singleton().getAddressSpaceSize(); - if (PhysicalMemory.isInitialized()) { - UnsignedWord physicalMemorySize = PhysicalMemory.getCachedSize(); - int maximumHeapSizePercent = HeapParameters.getMaximumHeapSizePercent(); - /* Do not cache because `-Xmx` option parsing may not have happened yet. */ - UnsignedWord result = physicalMemorySize.unsignedDivide(100).multiply(maximumHeapSizePercent); - if (result.belowThan(addressSpaceSize)) { - return result; - } + UnsignedWord physicalMemorySize = PhysicalMemory.size(); + int maximumHeapSizePercent = HeapParameters.getMaximumHeapSizePercent(); + /* Do not cache because `-Xmx` option parsing may not have happened yet. */ + UnsignedWord result = physicalMemorySize.unsignedDivide(100).multiply(maximumHeapSizePercent); + if (result.belowThan(addressSpaceSize)) { + return result; } return addressSpaceSize; } 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 fd77d1607339..d9ad77a0b0e7 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 @@ -402,11 +402,7 @@ private void printGCBefore(GCCause cause) { if (getCollectionEpoch().equal(0)) { printGCPrefixAndTime().string("Using ").string(getName()).newline(); Log log = printGCPrefixAndTime().spaces(2).string("Memory: "); - if (!PhysicalMemory.isInitialized()) { - log.string("unknown").newline(); - } else { - log.rational(PhysicalMemory.getCachedSize(), M, 0).string("M").newline(); - } + log.rational(PhysicalMemory.size(), M, 0).string("M").newline(); printGCPrefixAndTime().spaces(2).string("Heap policy: ").string(getPolicy().getName()).newline(); printGCPrefixAndTime().spaces(2).string("Maximum young generation size: ").rational(getPolicy().getMaximumYoungGenerationSize(), M, 0).string("M").newline(); printGCPrefixAndTime().spaces(2).string("Maximum heap size: ").rational(getPolicy().getMaximumHeapSize(), M, 0).string("M").newline(); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemorySupportImpl.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemorySupportImpl.java index ba1887f53c65..fdc4a28c3a38 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemorySupportImpl.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemorySupportImpl.java @@ -72,7 +72,7 @@ private static long getUsedSizeFromProcMemInfo() { List lines = readAllLines("/proc/meminfo"); for (String line : lines) { if (line.contains("MemAvailable")) { - return PhysicalMemory.getCachedSize().rawValue() - parseFirstNumber(line) * K; + return PhysicalMemory.size().rawValue() - parseFirstNumber(line) * K; } } } catch (Exception e) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentParser.java index e6ff9711586a..75760ec248ee 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/IsolateArgumentParser.java @@ -56,7 +56,7 @@ @AutomaticallyRegisteredImageSingleton public class IsolateArgumentParser { private static final RuntimeOptionKey[] OPTIONS = {SubstrateGCOptions.MinHeapSize, SubstrateGCOptions.MaxHeapSize, SubstrateGCOptions.MaxNewSize, SubstrateGCOptions.ReservedAddressSpaceSize, - SubstrateOptions.ConcealedOptions.AutomaticReferenceHandling, SubstrateOptions.ConcealedOptions.UsePerfData}; + SubstrateOptions.ConcealedOptions.AutomaticReferenceHandling, SubstrateOptions.ConcealedOptions.UsePerfData, SubstrateOptions.MaxRAM}; private static final int OPTION_COUNT = OPTIONS.length; private static final CGlobalData OPTION_NAMES = CGlobalDataFactory.createBytes(IsolateArgumentParser::createOptionNames); private static final CGlobalData OPTION_NAME_POSITIONS = CGlobalDataFactory.createBytes(IsolateArgumentParser::createOptionNamePosition); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java index 5499d7c620bf..02ddfa788b82 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java @@ -506,15 +506,6 @@ private static boolean pointsIntoNativeImageCode(CodePointer possibleIp) { return CodeInfoTable.lookupCodeInfo(possibleIp).isNonNull(); } - private static boolean isContainerized() { - boolean allowInit = !SubstrateOptions.AsyncSignalSafeDiagnostics.getValue(); - if (Container.singleton().isInitialized() || allowInit) { - return Container.singleton().isContainerized(); - } - // uninitialized and initialization not allowed - return false; - } - public static class FatalErrorState { AtomicWord diagnosticThread; volatile int diagnosticThunkIndex; @@ -864,7 +855,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev Platform platform = ImageSingletons.lookup(Platform.class); log.string("Platform: ").string(platform.getOS()).string("/").string(platform.getArchitecture()).newline(); log.string("Page size: ").unsigned(SubstrateOptions.getPageSize()).newline(); - log.string("Containerized: ").string(Container.singleton().isInitialized() ? String.valueOf(isContainerized()) : "unknown").newline(); + log.string("Containerized: ").string(String.valueOf(Container.singleton().isContainerized())).newline(); log.string("CPU features used for AOT compiled code: ").string(getBuildTimeCpuFeatures()).newline(); log.indent(false); } @@ -886,7 +877,7 @@ public int maxInvocationCount() { public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) { log.string("Runtime information:").indent(true); - if (isContainerized()) { + if (Container.singleton().isContainerized()) { log.string("CPU cores (container): "); int processorCount = getContainerActiveProcessorCount(); if (processorCount > 0) { @@ -904,7 +895,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev log.string("unknown").newline(); } - if (isContainerized()) { + if (Container.singleton().isContainerized()) { log.string("Memory (container): "); UnsignedWord memory = getContainerPhysicalMemory(); if (memory.aboveThan(0)) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/container/Container.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/container/Container.java index 050af828e479..d027983e673b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/container/Container.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/container/Container.java @@ -73,15 +73,8 @@ public static Container singleton() { return ImageSingletons.lookup(Container.class); } - @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - public boolean isInitialized() { - return STATE.get().readWord(0) != State.UNINITIALIZED; - } - /** - * Determines whether the image runs containerized, potentially initializing container support - * if not yet initialized. If initialization is not desired, calls to this method must be - * guarded by {@link #isInitialized()}. + * Determines whether the image runs containerized. */ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public boolean isContainerized() { @@ -90,16 +83,15 @@ public boolean isContainerized() { } UnsignedWord value = STATE.get().readWord(0); - if (value == State.UNINITIALIZED) { - value = initialize(); - } - assert value == State.CONTAINERIZED || value == State.NOT_CONTAINERIZED; return value == State.CONTAINERIZED; } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - private static UnsignedWord initialize() { + public static void initialize() { + if (!isSupported()) { + return; + } Pointer statePtr = STATE.get(); UnsignedWord value = statePtr.compareAndSwapWord(0, State.UNINITIALIZED, State.INITIALIZING, LocationIdentity.ANY_LOCATION); if (value == State.UNINITIALIZED) { @@ -126,12 +118,11 @@ private static UnsignedWord initialize() { VMError.guarantee(value != State.ERROR_LIBCONTAINER_TOO_OLD, "native-image tries to use a libsvm_container version that is too old"); VMError.guarantee(value != State.ERROR_LIBCONTAINER_TOO_NEW, "native-image tries to use a libsvm_container version that is too new"); VMError.guarantee(value == State.CONTAINERIZED || value == State.NOT_CONTAINERIZED, "unexpected libsvm_container initialize result"); - return value; } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public int getActiveProcessorCount() { - VMError.guarantee(isInitialized() && isContainerized()); + VMError.guarantee(isContainerized()); long currentMs = System.currentTimeMillis(); if (currentMs > activeProcessorCountTimeoutMs) { @@ -143,13 +134,13 @@ public int getActiveProcessorCount() { @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public int getCachedActiveProcessorCount() { - VMError.guarantee(isInitialized() && isContainerized()); + VMError.guarantee(isContainerized()); return cachedActiveProcessorCount; } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public UnsignedWord getPhysicalMemory() { - VMError.guarantee(isInitialized() && isContainerized()); + VMError.guarantee(isContainerized()); long currentMs = System.currentTimeMillis(); if (currentMs > physicalMemoryTimeoutMs) { @@ -161,13 +152,13 @@ public UnsignedWord getPhysicalMemory() { @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public UnsignedWord getCachedPhysicalMemory() { - VMError.guarantee(isInitialized() && isContainerized()); + VMError.guarantee(isContainerized()); return cachedPhysicalMemorySize; } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public long getMemoryLimitInBytes() { - VMError.guarantee(isInitialized() && isContainerized()); + VMError.guarantee(isContainerized()); long currentMs = System.currentTimeMillis(); if (currentMs > memoryLimitInBytesTimeoutMs) { @@ -179,7 +170,7 @@ public long getMemoryLimitInBytes() { @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public long getCachedMemoryLimitInBytes() { - VMError.guarantee(isInitialized() && isContainerized()); + VMError.guarantee(isContainerized()); return cachedMemoryLimitInBytes; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java index b686a01c5294..7a8e3873251c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java @@ -65,6 +65,7 @@ import com.oracle.svm.core.c.function.CEntryPointErrors; import com.oracle.svm.core.c.function.CEntryPointNativeFunctions; import com.oracle.svm.core.code.CodeInfoTable; +import com.oracle.svm.core.container.Container; import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; import com.oracle.svm.core.graal.nodes.CEntryPointEnterNode; import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode; @@ -231,6 +232,7 @@ private static int createIsolate(CEntryPointCreateIsolateParameters providedPara if (parameters.reservedSpaceSize().equal(0)) { parameters.setReservedSpaceSize(WordFactory.unsigned(parsedArgs.read(IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.ReservedAddressSpaceSize)))); } + Container.initialize(); WordPointer isolatePtr = StackValue.get(WordPointer.class); int error = Isolates.create(isolatePtr, parameters); @@ -309,6 +311,12 @@ private static int initializeIsolateInterruptibly0(CEntryPointCreateIsolateParam @NeverInline("GR-24649") private static int initializeIsolateInterruptibly1(CEntryPointCreateIsolateParameters parameters) { + /* + * Initialize the physical memory size. This must be done as early as possible because we + * must not trigger GC before PhysicalMemory is initialized. + */ + PhysicalMemory.initialize(); + /* * The VM operation thread must be started early as no VM operations can be scheduled before * this thread is fully started. The isolate teardown may also use VM operations. @@ -388,9 +396,6 @@ private static int initializeIsolateInterruptibly1(CEntryPointCreateIsolateParam /* Adjust stack overflow boundary of main thread. */ StackOverflowCheck.singleton().updateStackOverflowBoundary(); - /* Initialize the physical memory size. */ - PhysicalMemory.size(); - assert !isolateInitialized; isolateInitialized = true; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java index a6aaeff5ea2a..49200805f21a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java @@ -31,6 +31,7 @@ import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.IsolateArgumentParser; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.container.Container; @@ -67,7 +68,7 @@ default long usedSize() { private static UnsignedWord cachedSize = UNSET_SENTINEL; @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static boolean isInitialized() { + private static boolean isInitialized() { return cachedSize != UNSET_SENTINEL; } @@ -78,15 +79,15 @@ public static void setSize(UnsignedWord value) { } /** - * Returns the size of physical memory in bytes, querying it from the OS if it has not been - * initialized yet. + * Populates the cache for the size of physical memory in bytes, querying it from the OS if it + * has not been initialized yet. * * This method might allocate and use synchronization, so it is not safe to call it from inside * a VMOperation or during early stages of a thread or isolate. */ - public static UnsignedWord size() { + public static void initialize() { if (!isInitialized()) { - long memoryLimit = SubstrateOptions.MaxRAM.getValue(); + long memoryLimit = IsolateArgumentParser.getLongOptionValue(IsolateArgumentParser.getOptionIndex(SubstrateOptions.MaxRAM)); if (memoryLimit > 0) { cachedSize = WordFactory.unsigned(memoryLimit); } else if (Container.singleton().isContainerized()) { @@ -95,8 +96,6 @@ public static UnsignedWord size() { cachedSize = OperatingSystem.singleton().getPhysicalMemorySize(); } } - - return cachedSize; } /** Returns the amount of used physical memory in bytes, or -1 if not supported. */ @@ -113,11 +112,10 @@ public static long usedSize() { } /** - * Returns the size of physical memory in bytes that has been previously cached. This method - * must not be called if {@link #isInitialized()} is still false. + * Returns the size of physical memory in bytes that has been cached at startup. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static UnsignedWord getCachedSize() { + public static UnsignedWord size() { VMError.guarantee(isInitialized(), "Cached physical memory size is not available"); return cachedSize; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EveryChunkNativePeriodicEvents.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EveryChunkNativePeriodicEvents.java index 85bdfceee5bd..73e40876004c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EveryChunkNativePeriodicEvents.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EveryChunkNativePeriodicEvents.java @@ -87,7 +87,7 @@ private static void emitPhysicalMemory(long usedSize) { JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.PhysicalMemory); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); - JfrNativeEventWriter.putLong(data, PhysicalMemory.getCachedSize().rawValue()); + JfrNativeEventWriter.putLong(data, PhysicalMemory.size().rawValue()); JfrNativeEventWriter.putLong(data, usedSize); /* used size */ JfrNativeEventWriter.endSmallEvent(data); }