Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Shenandoah-specific changes.
  • Loading branch information
christianhaeubl committed Oct 15, 2025
commit e68c3da2a25fc1eb463bc5f0de3bb785dd59c4b3
2 changes: 1 addition & 1 deletion substratevm/mx.substratevm/mx_substratevm_namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
ignore_files = {"copy_x86.hpp", "copy_aarch64.hpp", "osThread_linux.hpp"}
ignore_includes = {"CPU_HEADER(copy)", "OS_HEADER(osThread)"}

files_with_cpp_guard = {"sharedGCStructs.h", "g1GCStructs.h"}
files_with_cpp_guard = {"sharedGCStructs.h", "shenandoahGCStructs.h", "g1GCStructs.h"}

SVM_NAMESPACE = "svm_namespace"

Expand Down
1 change: 1 addition & 0 deletions substratevm/mx.substratevm/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -2104,6 +2104,7 @@
"dependency:com.oracle.svm.native.libcontainer/*",
"file:debug/include",
"file:src/com.oracle.svm.core/src/com/oracle/svm/core/gc/shared/include",
"file:src/com.oracle.svm.core/src/com/oracle/svm/core/gc/shenandoah/include",
],
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.posix.linux.shenandoah;

import org.graalvm.word.UnsignedWord;

import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.gc.shenandoah.ShenandoahCommittedMemoryProvider;
import com.oracle.svm.core.gc.shenandoah.UseShenandoahGC;
import com.oracle.svm.core.heap.PhysicalMemory.PhysicalMemorySupport;
import com.oracle.svm.core.posix.linux.LinuxPhysicalMemorySupportImpl;
import com.oracle.svm.core.traits.BuiltinTraits.NoLayeredCallbacks;
import com.oracle.svm.core.traits.BuiltinTraits.RuntimeAccessOnly;
import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Disallowed;
import com.oracle.svm.core.traits.SingletonTraits;

@AutomaticallyRegisteredImageSingleton(value = PhysicalMemorySupport.class, onlyWith = UseShenandoahGC.class)
@SingletonTraits(access = RuntimeAccessOnly.class, layeredCallbacks = NoLayeredCallbacks.class, layeredInstallationKind = Disallowed.class)
public class ShenandoahPhysicalMemorySupport extends LinuxPhysicalMemorySupportImpl {
@Override
public UnsignedWord size() {
return ShenandoahCommittedMemoryProvider.getInstance().getPhysicalMemorySize();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ public HostedOptionKey<ReplacingLocatableMultiOptionValue.DelimitedString> multi
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, ReplacingLocatableMultiOptionValue.DelimitedString oldValue,
ReplacingLocatableMultiOptionValue.DelimitedString newValue) {

if (newValue.contains(GCOptionValue.G1.getValue())) {
if (newValue.contains(GCOptionValue.G1.getValue()) || newValue.contains(GCOptionValue.Shenandoah.getValue())) {
SubstrateOptions.SpawnIsolates.update(values, true);
SubstrateOptions.AllowVMInternalThreads.update(values, true);
SubstrateOptions.ConcealedOptions.UseDedicatedVMOperationThread.update(values, true);
Expand All @@ -619,6 +619,11 @@ public static boolean useG1GC() {
return SubstrateOptions.SupportedGCs.getValue().contains(GCOptionValue.G1.getValue());
}

@Fold
public static boolean useShenandoahGC() {
return SubstrateOptions.SupportedGCs.getValue().contains(GCOptionValue.Shenandoah.getValue());
}

public static class DeprecatedOptions {

@LayerVerifiedOption(kind = Kind.Changed, severity = Severity.Error)//
Expand Down Expand Up @@ -650,6 +655,21 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o
}
};

@LayerVerifiedOption(kind = Kind.Changed, severity = Severity.Error)//
@APIOption(name = "shenandoah", group = GCGroup.class, customHelp = "Shenandoah garbage collector")//
@Option(help = "Use the Shenandoah GC", deprecated = true, deprecationMessage = "Please use '--gc=shenandoah' instead")//
public static final HostedOptionKey<Boolean> UseShenandoahGC = new HostedOptionKey<>(false) {
@Override
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
if (newValue) {
SubstrateOptions.SupportedGCs.update(values, ReplacingLocatableMultiOptionValue.DelimitedString.buildWithCommaDelimiter(GCOptionValue.Shenandoah.getValue()));
} else if (!values.containsKey(SubstrateOptions.SupportedGCs) ||
((ReplacingLocatableMultiOptionValue.DelimitedString) values.get(SubstrateOptions.SupportedGCs)).contains(GCOptionValue.Shenandoah.getValue())) {
SubstrateOptions.SupportedGCs.update(values, ReplacingLocatableMultiOptionValue.DelimitedString.buildWithCommaDelimiter());
}
}
};

@Option(help = "The number of nanoseconds that the isolate teardown can take before warnings are printed. Disabled if less or equal to 0.", //
deprecated = true, deprecationMessage = "Use -XX:TearDownWarningSeconds=<secs> instead")//
public static final RuntimeOptionKey<Long> TearDownWarningNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,8 @@ private static void validatePlatformAndGC(SubstrateOptionKey<?> optionKey) {

if (!Platform.includedIn(Platform.LINUX_AMD64.class) && !Platform.includedIn(Platform.LINUX_AARCH64.class)) {
throw UserError.abort("The option '%s' can only be used on linux/amd64 or linux/aarch64.", optionKey.getName());
} else if (!SubstrateOptions.useG1GC()) {
throw UserError.abort("The option '%s' can only be used with the G1 ('--gc=G1') garbage collector.", optionKey.getName());
} else if (!SubstrateOptions.useG1GC() && !SubstrateOptions.useShenandoahGC()) {
throw UserError.abort("The option '%s' can only be used with the G1 ('--gc=G1') or the Shenandoah ('--gc=shenandoah') garbage collector.", optionKey.getName());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ public boolean getAsBoolean() {
}

public static boolean get() {
return SubstrateOptions.useG1GC();
return SubstrateOptions.useG1GC() || SubstrateOptions.useShenandoahGC();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.gc.shenandoah;

import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
import static com.oracle.svm.core.gc.shenandoah.ShenandoahOptions.ShenandoahRegionSize;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.c.type.CCharPointerPointer;
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;

import com.oracle.svm.core.IsolateArguments;
import com.oracle.svm.core.NeverInline;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.UnmanagedMemoryUtil;
import com.oracle.svm.core.c.function.CEntryPointErrors;
import com.oracle.svm.core.container.Container;
import com.oracle.svm.core.container.ContainerLibrary;
import com.oracle.svm.core.gc.shenandoah.nativelib.ShenandoahLibrary;
import com.oracle.svm.core.gc.shenandoah.nativelib.ShenandoahStructs.ShenandoahHeapOptions;
import com.oracle.svm.core.graal.snippets.CEntryPointSnippets;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.heap.ReferenceAccess;
import com.oracle.svm.core.os.AbstractCommittedMemoryProvider;
import com.oracle.svm.core.os.AbstractImageHeapProvider;
import com.oracle.svm.core.os.CommittedMemoryProvider;
import com.oracle.svm.core.os.ImageHeapProvider;
import com.oracle.svm.core.os.VirtualMemoryProvider;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.util.UnsignedUtils;

import jdk.graal.compiler.api.replacements.Fold;
import jdk.graal.compiler.core.common.CompressEncoding;
import jdk.graal.compiler.word.Word;

/**
* Reserves one contiguous block of memory in which the image heap and the collected Java heap are
* placed. The layout of this block of memory is as follows:
*
* <pre>
* | null regions | image heap | collected Java heap |
* | (protected) | closed | open | size determined by -Xms/-Xmx |
* ^
* heapBase
* </pre>
*
* <ul>
* <li>The memory right after the heap base is protected and cannot be accessed. This ensures that
* Java null values never point to valid objects.</li>
* <li>The image heap consists of closed and open regions (see {@link ShenandoahRegionType}).</li>
* <li>The size of the Java heap is determined by the min and max heap size values (-Xms, -Xmx) that
* are specified by the user. If uncompressed references are used, it is guaranteed that the image
* heap does not reduce the size of the Java heap, e.g., if the user specifies '-Xmx1g', then the
* Java heap will have a maximum size of 1g, regardless of the image heap size. However, if
* compressed references are used, the image heap and the Java heap need to coexist in the 32 GB
* address space, which can reduce the maximum size of the Java heap.</li>
* </ul>
*/
public class ShenandoahCommittedMemoryProvider extends AbstractCommittedMemoryProvider {
private Pointer reservedBegin;
private UnsignedWord reservedSize;
private UnsignedWord maxHeapSize;
private UnsignedWord physicalMemorySize;

@Platforms(Platform.HOSTED_ONLY.class)
public ShenandoahCommittedMemoryProvider() {
assert SubstrateOptions.SpawnIsolates.getValue();
}

@Override
@Uninterruptible(reason = "Still being initialized.")
public int initialize(WordPointer heapBaseOut, IsolateArguments arguments) {
int argc = arguments.getArgc();
CCharPointerPointer argv = arguments.getArgv();

UnsignedWord nullRegionSize = Word.unsigned(ShenandoahHeap.get().getImageHeapOffsetInAddressSpace());
// The image heap size in the file may be smaller than the image heap at run-time because we
// don't fill the last image heap region completely. This reduces the file size.
UnsignedWord imageHeapSize = UnsignedUtils.roundUp(AbstractImageHeapProvider.getImageHeapSizeInFile(), Word.unsigned(getRegionSize()));
UnsignedWord heapBaseAlignment = Word.unsigned(Heap.getHeap().getHeapBaseAlignment());

int heapOptionStructSize = SizeOf.get(ShenandoahHeapOptions.class);
ShenandoahHeapOptions heapOptions = StackValue.get(ShenandoahHeapOptions.class);
UnmanagedMemoryUtil.fill((Pointer) heapOptions, Word.unsigned(heapOptionStructSize), (byte) 0);

boolean isContainerized = Container.isSupported() && Container.singleton().isContainerized();
long containerMemoryLimitInBytes = isContainerized ? ContainerLibrary.getMemoryLimitInBytes() : 0;
int containerActiveProcessorCount = isContainerized ? ContainerLibrary.getActiveProcessorCount() : 0;

ShenandoahLibrary.parseOptions(ShenandoahLibrary.VERSION, argc, argv, ShenandoahOptions.HOSTED_ARGUMENTS.get(), ShenandoahOptions.RUNTIME_ARGUMENTS.get(),
ReferenceAccess.singleton().getMaxAddressSpaceSize(), heapBaseAlignment, nullRegionSize, imageHeapSize,
getCompressedReferenceShift(), isContainerized, containerMemoryLimitInBytes, containerActiveProcessorCount, heapOptions);

UnsignedWord heapAddressSpaceSize = heapOptions.heapAddressSpaceSize();
UnsignedWord newMaxHeapSize = heapOptions.maxHeapSize();
assert heapAddressSpaceSize.belowOrEqual(ReferenceAccess.singleton().getMaxAddressSpaceSize()) : "must be";

if (heapAddressSpaceSize.belowThan(nullRegionSize.add(imageHeapSize))) {
return CEntryPointErrors.INSUFFICIENT_ADDRESS_SPACE;
}

Pointer reservedMemory = reserveHeapMemory(heapAddressSpaceSize, heapBaseAlignment);
if (reservedMemory.isNull()) {
return CEntryPointErrors.RESERVE_ADDRESS_SPACE_FAILED;
}

WordPointer imageHeapEndOut = StackValue.get(WordPointer.class);
int result = ImageHeapProvider.get().initialize(reservedMemory, heapAddressSpaceSize, heapBaseOut, imageHeapEndOut);
if (result != CEntryPointErrors.NO_ERROR) {
VirtualMemoryProvider.get().free(reservedMemory, heapAddressSpaceSize);
return result;
}

CEntryPointSnippets.initBaseRegisters(heapBaseOut.read());
return initialize0(reservedMemory, heapAddressSpaceSize, newMaxHeapSize, heapOptions);
}

@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE)
@NeverInline("Force loading of a new instance reference, now that the heap base is initialized.")
@SuppressWarnings("hiding")
private static int initialize0(Pointer reservedBegin, UnsignedWord reservedSize, UnsignedWord maxHeapSize, ShenandoahHeapOptions heapOptions) {
ShenandoahCommittedMemoryProvider instance = getInstance();
instance.reservedBegin = reservedBegin;
instance.reservedSize = reservedSize;
instance.maxHeapSize = maxHeapSize;
instance.physicalMemorySize = heapOptions.physicalMemorySize();
return CEntryPointErrors.NO_ERROR;
}

@Uninterruptible(reason = "Still being initialized.")
private static Pointer reserveHeapMemory(UnsignedWord heapAddressSpaceSize, UnsignedWord heapBaseAlignment) {
return VirtualMemoryProvider.get().reserve(heapAddressSpaceSize, heapBaseAlignment, false);
}

@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
public static ShenandoahCommittedMemoryProvider getInstance() {
return (ShenandoahCommittedMemoryProvider) CommittedMemoryProvider.get();
}

@Override
public UnsignedWord getCollectedHeapAddressSpaceSize() {
Pointer collectedHeapStart = KnownIntrinsics.heapBase().add(getCollectedHeapOffsetInAddressSpace());
assert collectedHeapStart.aboveOrEqual(reservedBegin);
return reservedSize.subtract(collectedHeapStart.subtract(reservedBegin));
}

private static UnsignedWord getCollectedHeapOffsetInAddressSpace() {
return UnsignedUtils.roundUp(ImageHeapProvider.get().getImageHeapEndOffsetInAddressSpace(), Word.unsigned(getRegionSize()));
}

@Override
public UnsignedWord getReservedAddressSpaceSize() {
return reservedSize;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public UnsignedWord getMaxHeapSize() {
return maxHeapSize;
}

public UnsignedWord getPhysicalMemorySize() {
return physicalMemorySize;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public int getMaxRegions() {
long result = getMaxHeapSize().unsignedDivide(getRegionSize()).rawValue();
assert (int) result == result;
return (int) result;
}

@Fold
static int getCompressedReferenceShift() {
return ImageSingletons.lookup(CompressEncoding.class).getShift();
}

@Fold
protected static int getRegionSize() {
return ShenandoahRegionSize.getValue();
}

@Override
@Uninterruptible(reason = "Tear-down in progress.")
public int tearDown() {
/*
* ImageHeapProvider.freeImageHeap must not be called because the ImageHeapProvider did not
* allocate any memory for the image heap.
*/
return unmapAddressSpace(KnownIntrinsics.heapBase());
}

@Uninterruptible(reason = "Tear-down in progress.")
private int unmapAddressSpace(Pointer heapBase) {
assert heapBase.aboveOrEqual(reservedBegin) && heapBase.belowOrEqual(reservedBegin.add(getRegionSize()));
if (VirtualMemoryProvider.get().free(reservedBegin, reservedSize) != 0) {
return CEntryPointErrors.FREE_ADDRESS_SPACE_FAILED;
}
return CEntryPointErrors.NO_ERROR;
}
}
Loading
Loading