diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index a9ce2286a9c9..3cc9871eda86 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -400,6 +400,7 @@ ], "requiresConcealed" : { "java.base": [ + "jdk.internal.misc", "sun.nio.ch", ], "java.management": [ 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 7758d3083943..24fea741df28 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 @@ -24,9 +24,6 @@ */ package com.oracle.svm.core.genscavenge; -import java.util.concurrent.atomic.AtomicBoolean; - -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.UnsignedWord; @@ -36,13 +33,20 @@ import com.oracle.svm.core.heap.PhysicalMemory; import com.oracle.svm.core.heap.ReferenceAccess; import com.oracle.svm.core.jdk.UninterruptibleUtils; +import com.oracle.svm.core.os.CommittedMemoryProvider; +import com.oracle.svm.core.thread.JavaSpinLockUtils; import com.oracle.svm.core.thread.VMOperation; 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; +import jdk.graal.compiler.word.Word; +import jdk.internal.misc.Unsafe; +/** + * Note that a lot of methods in this class are final. Subclasses may only override certain methods + * to avoid inconsistencies between the different heap size values. + */ abstract class AbstractCollectionPolicy implements CollectionPolicy { protected static final int MIN_SPACE_SIZE_IN_ALIGNED_CHUNKS = 8; @@ -54,6 +58,9 @@ static int getMaxSurvivorSpaces(Integer userValue) { return (userValue != null) ? userValue : AbstractCollectionPolicy.MAX_TENURING_THRESHOLD; } + private static final Unsafe U = Unsafe.getUnsafe(); + private static final long SIZES_UPDATE_LOCK_OFFSET = U.objectFieldOffset(AbstractCollectionPolicy.class, "sizesUpdateLock"); + /* * Constants that can be made options if desirable. These are -XX options in HotSpot, refer to * their descriptions for details. The values are HotSpot defaults unless labeled otherwise. @@ -78,8 +85,8 @@ static int getMaxSurvivorSpaces(Integer userValue) { protected UnsignedWord oldSize; protected int tenuringThreshold; - protected volatile SizeParameters sizes; - private final AtomicBoolean sizesUpdateSpinLock = new AtomicBoolean(); + protected volatile SizeParameters sizes = null; + @SuppressWarnings("unused") private volatile int sizesUpdateLock; protected AbstractCollectionPolicy(int initialNewRatio, int initialTenuringThreshold) { this.initialNewRatio = initialNewRatio; @@ -145,40 +152,49 @@ protected void guaranteeSizeParametersInitialized() { @Override public void updateSizeParameters() { - SizeParameters params = computeSizeParameters(sizes); - SizeParameters previous = sizes; - if (previous != null && params.equal(previous)) { + /* + * Read the old object before computing the new values. Otherwise, we risk reusing an + * outdated SizeParameters object. + */ + SizeParameters prevParams = sizes; + SizeParameters newParams = computeSizeParameters(prevParams); + if (prevParams != null && newParams.equal(prevParams)) { return; // nothing to do } - while (!sizesUpdateSpinLock.compareAndSet(false, true)) { - /* - * We use a primitive spin lock because at this point, the current thread might be - * unable to use a Java lock (e.g. no Thread object yet), and the critical section is - * short, so we do not want to suspend and wake up threads for it. - */ - PauseNode.pause(); - } + updateSizeParameters0(newParams, prevParams); + guaranteeSizeParametersInitialized(); // sanity + } + + @Uninterruptible(reason = "Holding the spin lock at a safepoint can result in deadlocks.") + private void updateSizeParameters0(SizeParameters newParams, SizeParameters prevParams) { + /* + * We use a primitive spin lock because at this point, the current thread might be unable to + * use a Java lock (e.g. no Thread object yet), and the critical section is short, so we do + * not want to suspend and wake up threads for it. + */ + JavaSpinLockUtils.lockNoTransition(this, SIZES_UPDATE_LOCK_OFFSET); try { - updateSizeParametersLocked(params, previous); + if (sizes != prevParams) { + /* + * Some other thread beat us and we cannot tell if our values or their values are + * newer, so back off - any newer values will be applied eventually. + */ + return; + } + updateSizeParametersLocked(newParams, prevParams); } finally { - sizesUpdateSpinLock.set(false); + JavaSpinLockUtils.unlock(this, SIZES_UPDATE_LOCK_OFFSET); } - guaranteeSizeParametersInitialized(); // sanity } - @Uninterruptible(reason = "Must be atomic with regard to garbage collection.") - private void updateSizeParametersLocked(SizeParameters params, SizeParameters previous) { - if (sizes != previous) { - // Some other thread beat us and we cannot tell if our values or their values are newer, - // so back off -- any newer values will be applied eventually. - return; - } - sizes = params; + @Uninterruptible(reason = "Holding the spin lock at a safepoint can result in deadlocks. Updating the size parameters must be atomic with regard to garbage collection.") + private void updateSizeParametersLocked(SizeParameters newParams, SizeParameters prevParams) { + sizes = newParams; - if (previous == null || gcCount() == 0) { - survivorSize = params.initialSurvivorSize; - edenSize = params.initialEdenSize; - oldSize = params.initialOldSize(); + if (prevParams == null || gcCount() == 0) { + survivorSize = newParams.initialSurvivorSize; + edenSize = newParams.initialEdenSize; + oldSize = newParams.initialOldSize(); promoSize = UnsignedUtils.min(edenSize, oldSize); } @@ -191,51 +207,51 @@ private void updateSizeParametersLocked(SizeParameters params, SizeParameters pr * We assume that such changes happen very early on and values then adapt reasonably quick, * but we must still ensure that computations can handle it (for example, no overflows). */ - survivorSize = UnsignedUtils.min(survivorSize, params.maxSurvivorSize()); + survivorSize = UnsignedUtils.min(survivorSize, newParams.maxSurvivorSize()); edenSize = UnsignedUtils.min(edenSize, getMaximumEdenSize()); - oldSize = UnsignedUtils.min(oldSize, params.maxOldSize()); - promoSize = UnsignedUtils.min(promoSize, params.maxOldSize()); + oldSize = UnsignedUtils.min(oldSize, newParams.maxOldSize()); + promoSize = UnsignedUtils.min(promoSize, newParams.maxOldSize()); } @Override - public UnsignedWord getInitialEdenSize() { + public final UnsignedWord getInitialEdenSize() { guaranteeSizeParametersInitialized(); return sizes.initialEdenSize; } @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public UnsignedWord getMaximumEdenSize() { + public final UnsignedWord getMaximumEdenSize() { guaranteeSizeParametersInitialized(); return alignDown(sizes.maxYoungSize.subtract(survivorSize.multiply(2))); } @Override - public UnsignedWord getMaximumHeapSize() { + public final UnsignedWord getMaximumHeapSize() { guaranteeSizeParametersInitialized(); return sizes.maxHeapSize; } @Override - public UnsignedWord getMaximumYoungGenerationSize() { + public final UnsignedWord getMaximumYoungGenerationSize() { guaranteeSizeParametersInitialized(); return sizes.maxYoungSize; } @Override - public UnsignedWord getInitialSurvivorSize() { + public final UnsignedWord getInitialSurvivorSize() { guaranteeSizeParametersInitialized(); return sizes.initialSurvivorSize; } @Override - public UnsignedWord getMaximumSurvivorSize() { + public final UnsignedWord getMaximumSurvivorSize() { guaranteeSizeParametersInitialized(); return sizes.maxSurvivorSize(); } @Override - public UnsignedWord getCurrentHeapCapacity() { + public final UnsignedWord getCurrentHeapCapacity() { assert VMOperation.isGCInProgress() : "use only during GC"; guaranteeSizeParametersInitialized(); return edenSize.add(survivorSize).add(oldSize); @@ -243,7 +259,7 @@ public UnsignedWord getCurrentHeapCapacity() { @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public UnsignedWord getSurvivorSpacesCapacity() { + public final UnsignedWord getSurvivorSpacesCapacity() { assert VMOperation.isGCInProgress() : "use only during GC"; guaranteeSizeParametersInitialized(); return survivorSize; @@ -251,25 +267,25 @@ public UnsignedWord getSurvivorSpacesCapacity() { @Override @Uninterruptible(reason = "Ensure reading a consistent value.") - public UnsignedWord getYoungGenerationCapacity() { + public final UnsignedWord getYoungGenerationCapacity() { guaranteeSizeParametersInitialized(); return edenSize.add(survivorSize); } @Override - public UnsignedWord getInitialOldSize() { + public final UnsignedWord getInitialOldSize() { guaranteeSizeParametersInitialized(); return sizes.initialOldSize(); } @Override - public UnsignedWord getMaximumOldSize() { + public final UnsignedWord getMaximumOldSize() { guaranteeSizeParametersInitialized(); return sizes.maxOldSize(); } @Override - public UnsignedWord getOldGenerationCapacity() { + public final UnsignedWord getOldGenerationCapacity() { guaranteeSizeParametersInitialized(); return oldSize; } @@ -308,7 +324,7 @@ public void onCollectionBegin(boolean completeCollection, long requestingNanoTim } @Override - public UnsignedWord getMinimumHeapSize() { + public final UnsignedWord getMinimumHeapSize() { return sizes.minHeapSize; } @@ -316,7 +332,6 @@ public UnsignedWord getMinimumHeapSize() { protected abstract long gcCount(); protected SizeParameters computeSizeParameters(SizeParameters existing) { - UnsignedWord addressSpaceSize = ReferenceAccess.singleton().getAddressSpaceSize(); UnsignedWord minYoungSpaces = minSpaceSize(); // eden if (HeapParameters.getMaxSurvivorSpaces() > 0) { minYoungSpaces = minYoungSpaces.add(minSpaceSize().multiply(2)); // survivor from and to @@ -331,7 +346,7 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) { maxHeap = PhysicalMemory.size().unsignedDivide(100).multiply(HeapParameters.getMaximumHeapSizePercent()); } UnsignedWord unadjustedMaxHeap = maxHeap; - maxHeap = UnsignedUtils.clamp(alignDown(maxHeap), minAllSpaces, alignDown(addressSpaceSize)); + maxHeap = UnsignedUtils.clamp(alignDown(maxHeap), minAllSpaces, alignDown(getHeapSizeLimit())); UnsignedWord maxYoung; long optionMaxYoung = SubstrateGCOptions.MaxNewSize.getValue(); @@ -342,11 +357,11 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) { } else { maxYoung = maxHeap.unsignedDivide(AbstractCollectionPolicy.NEW_RATIO + 1); } - maxYoung = UnsignedUtils.clamp(alignDown(maxYoung), minYoungSpaces, maxHeap.subtract(minSpaceSize())); + maxYoung = UnsignedUtils.clamp(alignDown(maxYoung), minYoungSpaces, getYoungSizeLimit(maxHeap)); UnsignedWord maxOld = alignDown(maxHeap.subtract(maxYoung)); maxHeap = maxYoung.add(maxOld); - VMError.guarantee(maxOld.aboveOrEqual(minSpaceSize()) && maxHeap.belowOrEqual(addressSpaceSize) && + VMError.guarantee(maxOld.aboveOrEqual(minSpaceSize()) && maxHeap.belowOrEqual(getHeapSizeLimit()) && (maxHeap.belowOrEqual(unadjustedMaxHeap) || unadjustedMaxHeap.belowThan(minAllSpaces))); UnsignedWord minHeap = Word.zero(); @@ -384,6 +399,14 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) { return SizeParameters.get(existing, maxHeap, maxYoung, initialHeap, initialEden, initialSurvivor, minHeap); } + protected UnsignedWord getHeapSizeLimit() { + return CommittedMemoryProvider.get().getCollectedHeapAddressSpaceSize(); + } + + protected UnsignedWord getYoungSizeLimit(UnsignedWord maxHeap) { + return maxHeap.subtract(minSpaceSize()); + } + protected UnsignedWord getInitialHeapSize() { return AbstractCollectionPolicy.INITIAL_HEAP_SIZE; } @@ -419,7 +442,7 @@ private SizeParameters(UnsignedWord maxHeapSize, UnsignedWord maxYoungSize, Unsi assert initialHeapSize.belowOrEqual(maxHeapSize); assert maxSurvivorSize().belowThan(maxYoungSize); assert maxYoungSize.add(maxOldSize()).equal(maxHeapSize); - assert maxHeapSize.belowOrEqual(ReferenceAccess.singleton().getAddressSpaceSize()); + assert maxHeapSize.belowOrEqual(ReferenceAccess.singleton().getMaxAddressSpaceSize()); assert initialEdenSize.add(initialSurvivorSize.multiply(2)).equal(initialYoungSize()); assert initialYoungSize().add(initialOldSize()).equal(initialHeapSize); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AddressRangeCommittedMemoryProvider.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AddressRangeCommittedMemoryProvider.java index 2e9d6cf82f76..59bcd030ba10 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AddressRangeCommittedMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AddressRangeCommittedMemoryProvider.java @@ -28,10 +28,9 @@ import static com.oracle.svm.core.util.PointerUtils.roundDown; import static com.oracle.svm.core.util.PointerUtils.roundUp; import static com.oracle.svm.core.util.VMError.guarantee; -import static jdk.graal.compiler.word.Word.unsigned; import static jdk.graal.compiler.word.Word.nullPointer; +import static jdk.graal.compiler.word.Word.unsigned; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.Isolate; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -74,6 +73,7 @@ import com.oracle.svm.core.util.VMError; import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.word.Word; /** * Reserves a fixed-size address range and provides memory from it by committing and uncommitting @@ -144,16 +144,12 @@ public AddressRangeCommittedMemoryProvider() { @Override @Uninterruptible(reason = "Still being initialized.") public int initialize(WordPointer heapBasePointer, IsolateArguments arguments) { - UnsignedWord addressSpaceSize = ReferenceAccess.singleton().getAddressSpaceSize(); + UnsignedWord maxAddressSpaceSize = ReferenceAccess.singleton().getMaxAddressSpaceSize(); UnsignedWord reserved = Word.unsigned(IsolateArgumentAccess.readLong(arguments, IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.ReservedAddressSpaceSize))); if (reserved.equal(0)) { - /* - * By default, always reserve at least 32 GB of address space. If a large maximum heap - * size was specified, then reserve 2x of that maximum heap size (assuming that the max. - * address space size is large enough for that). - */ - UnsignedWord maxHeapSize = Word.unsigned(IsolateArgumentAccess.readLong(arguments, IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.MaxHeapSize))).multiply(2); - reserved = UnsignedUtils.clamp(maxHeapSize, Word.unsigned(MIN_RESERVED_ADDRESS_SPACE_SIZE), addressSpaceSize); + /* Reserve a 32 GB address space, except if a larger heap size was specified. */ + UnsignedWord maxHeapSize = Word.unsigned(IsolateArgumentAccess.readLong(arguments, IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.MaxHeapSize))); + reserved = UnsignedUtils.clamp(maxHeapSize, Word.unsigned(MIN_RESERVED_ADDRESS_SPACE_SIZE), maxAddressSpaceSize); } UnsignedWord alignment = unsigned(Heap.getHeap().getPreferredAddressSpaceAlignment()); @@ -790,7 +786,8 @@ private boolean isInAllocList(FreeListNode node) { return false; } - public UnsignedWord getReservedSpaceSize() { + @Override + public UnsignedWord getReservedAddressSpaceSize() { return reservedSpaceSize; } 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 ea442a7a91e9..3a33256131b2 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 @@ -26,7 +26,6 @@ import static com.oracle.svm.core.genscavenge.CollectionPolicy.shouldCollectYoungGenSeparately; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.UnsignedWord; @@ -35,11 +34,13 @@ 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.os.CommittedMemoryProvider; import com.oracle.svm.core.util.TimeUtils; import com.oracle.svm.core.util.UnsignedUtils; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.word.Word; + /** Basic/legacy garbage collection policies. */ final class BasicCollectionPolicies { @Platforms(Platform.HOSTED_ONLY.class) @@ -101,15 +102,9 @@ public final UnsignedWord getMaximumHeapSize() { return Word.unsigned(runtimeValue); } - /* - * If the physical size is known yet, the maximum size of the heap is a fraction of the - * size of the physical memory. - */ - UnsignedWord addressSpaceSize = ReferenceAccess.singleton().getAddressSpaceSize(); - UnsignedWord physicalMemorySize = PhysicalMemory.size(); + UnsignedWord addressSpaceSize = CommittedMemoryProvider.get().getCollectedHeapAddressSpaceSize(); int maximumHeapSizePercent = HeapParameters.getMaximumHeapSizePercent(); - /* Do not cache because `-Xmx` option parsing may not have happened yet. */ - UnsignedWord result = physicalMemorySize.unsignedDivide(100).multiply(maximumHeapSizePercent); + UnsignedWord result = PhysicalMemory.size().unsignedDivide(100).multiply(maximumHeapSizePercent); return UnsignedUtils.min(result, addressSpaceSize); } 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 6db2f15e3ea1..c872928eba9e 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 @@ -24,7 +24,6 @@ */ package com.oracle.svm.core.genscavenge; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.UnsignedWord; @@ -36,9 +35,11 @@ import com.oracle.svm.core.util.UserError; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.word.Word; + /** The interface for a garbage collection policy. All sizes are in bytes. */ public interface CollectionPolicy { - UnsignedWord UNDEFINED = Word.unsigned(-1); + UnsignedWord UNDEFINED = Word.unsigned(-1L); @Platforms(Platform.HOSTED_ONLY.class) static String getInitialPolicyName() { 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 66e7946b9bca..6a887b4efa4f 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 @@ -407,7 +407,7 @@ private void printGCBefore(GCCause cause) { printGCPrefixAndTime().string("Using ").string(getName()).newline(); Log log = printGCPrefixAndTime().spaces(2).string("Memory: "); log.rational(PhysicalMemory.size(), M, 0).string("M").newline(); - printGCPrefixAndTime().spaces(2).string("Heap policy: ").string(getPolicy().getName()).newline(); + printGCPrefixAndTime().spaces(2).string("GC 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(); printGCPrefixAndTime().spaces(2).string("Minimum heap size: ").rational(getPolicy().getMinimumHeapSize(), M, 0).string("M").newline(); 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 05bbb708528d..45652b26c91f 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 @@ -26,7 +26,6 @@ import org.graalvm.word.UnsignedWord; -import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.util.UnsignedUtils; @@ -61,8 +60,7 @@ public static final class Options { * See class javadoc for rationale behind this 16G limit. */ protected static final UnsignedWord MAXIMUM_HEAP_SIZE = Word.unsigned(16L * 1024L * 1024L * 1024L); - - protected static final UnsignedWord MAXIMUM_EDEN_SIZE = Word.unsigned(4L * 1024L * 1024L * 1024L); + protected static final UnsignedWord MAXIMUM_YOUNG_SIZE = Word.unsigned(5L * 1024L * 1024L * 1024L); private UnsignedWord sizeBefore = Word.zero(); private GCCause lastGCCause = null; @@ -97,14 +95,13 @@ protected UnsignedWord getInitialHeapSize() { } @Override - public UnsignedWord getMaximumHeapSize() { - return UnsignedUtils.min(super.getMaximumHeapSize(), MAXIMUM_HEAP_SIZE); + protected UnsignedWord getHeapSizeLimit() { + return UnsignedUtils.min(super.getHeapSizeLimit(), MAXIMUM_HEAP_SIZE); } @Override - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public UnsignedWord getMaximumEdenSize() { - return UnsignedUtils.min(super.getMaximumEdenSize(), MAXIMUM_EDEN_SIZE); + protected UnsignedWord getYoungSizeLimit(UnsignedWord maxHeap) { + return UnsignedUtils.min(super.getYoungSizeLimit(maxHeap), MAXIMUM_YOUNG_SIZE); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java index 497a1ce6e621..77274b9bc393 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java @@ -35,7 +35,6 @@ import java.util.List; import java.util.function.BooleanSupplier; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Isolate; @@ -80,6 +79,8 @@ import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.word.Word; + @InternalVMMethod public class JavaMainWrapper { /* @@ -218,6 +219,11 @@ private static int runCore0() { VMRuntime.initialize(); } + if (SubstrateOptions.PrintVMInfoAndExit.getValue()) { + printVmInfo(); + return 0; + } + if (SubstrateOptions.DumpHeapAndExit.getValue()) { return VMInspectionOptions.dumpImageHeap() ? 0 : 1; } @@ -438,6 +444,12 @@ public static String getCRuntimeArgument0() { return CTypeConversion.toJavaString(MAIN_ISOLATE_PARAMETERS.get().getArgv().read(0)); } + private static void printVmInfo() { + VM vm = ImageSingletons.lookup(VM.class); + System.out.println(vm.formattedVmVersion); + System.out.println(vm.formattedJdkVersion); + } + private static final class EnterCreateIsolateWithCArgumentsPrologue implements CEntryPointOptions.Prologue { private static final CGlobalData errorMessage = CGlobalDataFactory.createCString( "Failed to create the main Isolate."); 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 6b1875d20059..6b0a3c15355d 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 @@ -860,7 +860,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev log.string("Build time information:").indent(true); VM vm = ImageSingletons.lookup(VM.class); - log.string("Version: ").string(vm.vendorVersion).string(" (").string(vm.info).string("), JDK ").string(vm.version).newline(); + log.string("Version: ").string(vm.formattedVmVersion).string(", ").string(vm.formattedJdkVersion).newline(); Platform platform = ImageSingletons.lookup(Platform.class); log.string("Platform: ").string(platform.getOS()).string("/").string(platform.getArchitecture()).newline(); 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 b8a9f5df9415..90364729fc7b 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 @@ -1172,6 +1172,9 @@ public Boolean getValue(OptionValues values) { @Option(help = "Create a heap dump and exit.")// public static final RuntimeOptionKey DumpHeapAndExit = new RuntimeOptionKey<>(false, Immutable); + @Option(help = "Print some VM information and exit.")// + public static final RuntimeOptionKey PrintVMInfoAndExit = new RuntimeOptionKey<>(false, Immutable); + @Option(help = "Enable Java Flight Recorder.")// public static final RuntimeOptionKey FlightRecorder = new RuntimeOptionKey<>(false, Immutable); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VM.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VM.java index 7775770de9af..8ec72885f360 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VM.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VM.java @@ -35,6 +35,10 @@ public final class VM { public final String vendorUrl; public final String vendorVersion; + /* Preformatted version strings based on the values above. */ + public final String formattedVmVersion; + public final String formattedJdkVersion; + @Platforms(Platform.HOSTED_ONLY.class) public VM(String vmInfo) { info = vmInfo; @@ -42,6 +46,9 @@ public VM(String vmInfo) { vendor = getVendor(); vendorUrl = getVendorUrl(); vendorVersion = getVendorVersion(); + + formattedVmVersion = vendorVersion + " (" + info + ")"; + formattedJdkVersion = "JDK " + version; } @Platforms(Platform.HOSTED_ONLY.class) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/dcmd/VMVersionDmd.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/dcmd/VMVersionDmd.java index b7165229630b..2f9b098e44b1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/dcmd/VMVersionDmd.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/dcmd/VMVersionDmd.java @@ -43,6 +43,6 @@ public VMVersionDmd() { @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+18/src/hotspot/share/services/diagnosticCommand.cpp#L234-L246") public String execute(DCmdArguments args) throws Throwable { VM vm = ImageSingletons.lookup(VM.class); - return vm.vendorVersion + " (" + vm.info + ")" + System.lineSeparator() + "JDK " + vm.version; + return vm.formattedVmVersion + System.lineSeparator() + vm.formattedJdkVersion; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/HeapSizeVerifier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/HeapSizeVerifier.java index 92b3972fb19f..a6f1a57922fb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/HeapSizeVerifier.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/HeapSizeVerifier.java @@ -24,7 +24,6 @@ */ package com.oracle.svm.core.heap; -import jdk.graal.compiler.word.Word; import org.graalvm.word.UnsignedWord; import com.oracle.svm.core.SubstrateGCOptions; @@ -34,6 +33,8 @@ import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.UserError.UserException; +import jdk.graal.compiler.word.Word; + /** * Verifies that the heap size options are used consistently. Note that some checks seem redundant * at first glance. However, those checks are needed because options don't necessarily have a value. @@ -72,7 +73,8 @@ private static void verifyMinHeapSize() { UnsignedWord maxHeapSize = Word.unsigned(SubstrateGCOptions.MaxHeapSize.getValue()); if (maxHeapSize.notEqual(0) && minHeapSize.aboveThan(maxHeapSize)) { - throwError(minHeapSize, MIN_HEAP_SIZE_NAME, maxHeapSize, MAX_HEAP_SIZE_NAME); + String message = formatError(minHeapSize, MIN_HEAP_SIZE_NAME, maxHeapSize, MAX_HEAP_SIZE_NAME); + throw reportError(message); } } @@ -83,7 +85,8 @@ private static void verifyMaxNewSize() { UnsignedWord maxHeapSize = Word.unsigned(SubstrateGCOptions.MaxHeapSize.getValue()); if (maxHeapSize.notEqual(0) && maxNewSize.aboveThan(maxHeapSize)) { - throwError(maxNewSize, MAX_NEW_SIZE_NAME, maxHeapSize, MAX_HEAP_SIZE_NAME); + String message = formatError(maxNewSize, MAX_NEW_SIZE_NAME, maxHeapSize, MAX_HEAP_SIZE_NAME); + throw reportError(message); } } @@ -100,26 +103,33 @@ public static void verifyMaxNewSizeAgainstMaxAddressSpaceSize(UnsignedWord maxNe } private static void verifyAgainstMaxAddressSpaceSize(UnsignedWord actualValue, String actualValueName) { - UnsignedWord maxAddressSpaceSize = ReferenceAccess.singleton().getAddressSpaceSize(); + UnsignedWord maxAddressSpaceSize = ReferenceAccess.singleton().getMaxAddressSpaceSize(); if (actualValue.aboveThan(maxAddressSpaceSize)) { - throwError(actualValue, actualValueName, maxAddressSpaceSize, "largest possible heap address space"); + String message = formatError(actualValue, actualValueName, maxAddressSpaceSize, "largest possible heap address space"); + if (ReferenceAccess.singleton().getCompressionShift() > 0) { + message += " To allow larger values, please disable compressed references when building the image by adding the option '-H:-UseCompressedReferences'"; + } + throw reportError(message); } } private static void verifyAgainstReservedAddressSpaceSize(UnsignedWord actualValue, String actualValueName) { UnsignedWord reservedAddressSpaceSize = Word.unsigned(SubstrateGCOptions.ReservedAddressSpaceSize.getValue()); if (reservedAddressSpaceSize.notEqual(0) && actualValue.aboveThan(reservedAddressSpaceSize)) { - throwError(actualValue, actualValueName, reservedAddressSpaceSize, "value of the option '" + SubstrateGCOptions.ReservedAddressSpaceSize.getName() + "'"); + String message = formatError(actualValue, actualValueName, reservedAddressSpaceSize, SubstrateGCOptions.ReservedAddressSpaceSize.getName()); + throw reportError(message); } } - private static void throwError(UnsignedWord actualValue, String actualValueName, UnsignedWord maxValue, String maxValueName) throws UserException { + private static RuntimeException reportError(String message) throws UserException { if (SubstrateUtil.HOSTED) { - throw UserError.abort("The specified %s (%s) must not be larger than the %s (%s).", actualValueName, format(actualValue), maxValueName, format(maxValue)); - } else { - throw new IllegalArgumentException( - "The specified " + actualValueName + " (" + format(actualValue) + ") must not be larger than the " + maxValueName + " (" + format(maxValue) + ")."); + throw UserError.abort(message); } + throw new IllegalArgumentException(message); + } + + private static String formatError(UnsignedWord actualValue, String actualValueName, UnsignedWord maxValue, String maxValueName) { + return "The specified " + actualValueName + " (" + format(actualValue) + ") must not be larger than the " + maxValueName + " (" + format(maxValue) + ")."; } private static String format(UnsignedWord bytes) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceAccess.java index 0fc4355e8d57..db41582f0275 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceAccess.java @@ -41,7 +41,7 @@ *

* Accessing hub references involves the reserved GC bits, compression shift and object alignment * and is defined by {@link SubstrateBasicLoweringProvider#createReadHub}. - * + *

* Regular references just require the heapbase register (for -H:+SpawnIsolates) and compression * shift (for -H:+UseCompressedReferences) *

@@ -103,5 +103,5 @@ static ReferenceAccess singleton() { * Returns the maximum size that the Java heap address space can have at run-time (e.g., based * on the reference size). */ - UnsignedWord getAddressSpaceSize(); + UnsignedWord getMaxAddressSpaceSize(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceAccessImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceAccessImpl.java index 0afb66052c16..a1c4f1c4be95 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceAccessImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceAccessImpl.java @@ -109,7 +109,7 @@ public CompressEncoding getCompressEncoding() { @Fold @Override - public UnsignedWord getAddressSpaceSize() { + public UnsignedWord getMaxAddressSpaceSize() { int compressionShift = ReferenceAccess.singleton().getCompressEncoding().getShift(); if (compressionShift > 0) { int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCommittedMemoryProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCommittedMemoryProvider.java index 1d5e927182d5..194bfcef00ba 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCommittedMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCommittedMemoryProvider.java @@ -30,7 +30,6 @@ import static com.oracle.svm.core.Isolates.IMAGE_HEAP_WRITABLE_END; import static jdk.graal.compiler.word.Word.nullPointer; -import jdk.graal.compiler.word.Word; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; @@ -45,6 +44,8 @@ import com.oracle.svm.core.util.UnsignedUtils; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.word.Word; + public abstract class AbstractCommittedMemoryProvider implements CommittedMemoryProvider { @Uninterruptible(reason = "Still being initialized.") protected static int protectSingleIsolateImageHeap() { @@ -123,4 +124,15 @@ protected static void free(PointerBase start, UnsignedWord nbytes, NmtCategory n int result = VirtualMemoryProvider.get().free(start, nbytes); VMError.guarantee(result == 0, "Error while freeing virtual memory."); } + + @Override + public UnsignedWord getCollectedHeapAddressSpaceSize() { + /* Only a part of the address space is available for the collected Java heap. */ + UnsignedWord reservedAddressSpace = getReservedAddressSpaceSize(); + UnsignedWord imageHeapSize = Heap.getHeap().getImageHeapReservedBytes(); + assert reservedAddressSpace.aboveThan(imageHeapSize); + return reservedAddressSpace.subtract(imageHeapSize); + } + + protected abstract UnsignedWord getReservedAddressSpaceSize(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java index a7b01f526f33..4f974343496d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java @@ -72,6 +72,13 @@ default UnsignedWord getGranularity() { return VirtualMemoryProvider.get().getGranularity(); } + /** + * Returns the size of the address space that is reserved for the collected Java heap (i.e., + * this explicitly excludes all heap parts that are not collected, such as the image heap or the + * protected memory before the image heap). + */ + UnsignedWord getCollectedHeapAddressSpaceSize(); + Pointer allocateExecutableMemory(UnsignedWord nbytes, UnsignedWord alignment); @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/OSCommittedMemoryProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/OSCommittedMemoryProvider.java index 98318d2f801c..c3d171cced8e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/OSCommittedMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/OSCommittedMemoryProvider.java @@ -30,13 +30,19 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.type.WordPointer; +import org.graalvm.word.UnsignedWord; import com.oracle.svm.core.IsolateArguments; import com.oracle.svm.core.Isolates; +import com.oracle.svm.core.SubstrateGCOptions; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.c.function.CEntryPointErrors; +import com.oracle.svm.core.heap.ReferenceAccess; import com.oracle.svm.core.snippets.KnownIntrinsics; +import com.oracle.svm.core.util.UnsignedUtils; + +import jdk.graal.compiler.word.Word; public class OSCommittedMemoryProvider extends ChunkBasedCommittedMemoryProvider { @Platforms(Platform.HOSTED_ONLY.class) @@ -64,4 +70,14 @@ public int tearDown() { } return ImageHeapProvider.get().freeImageHeap(KnownIntrinsics.heapBase()); } + + @Override + public UnsignedWord getReservedAddressSpaceSize() { + UnsignedWord maxAddressSpaceSize = ReferenceAccess.singleton().getMaxAddressSpaceSize(); + UnsignedWord optionValue = Word.unsigned(SubstrateGCOptions.ReservedAddressSpaceSize.getValue()); + if (optionValue.notEqual(0)) { + return UnsignedUtils.min(optionValue, maxAddressSpaceSize); + } + return maxAddressSpaceSize; + } }