Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions substratevm/mx.substratevm/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@
],
"requiresConcealed" : {
"java.base": [
"jdk.internal.misc",
"sun.nio.ch",
],
"java.management": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

Expand All @@ -191,85 +207,85 @@ 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);
}

@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;
}

@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;
}
Expand Down Expand Up @@ -308,15 +324,14 @@ public void onCollectionBegin(boolean completeCollection, long requestingNanoTim
}

@Override
public UnsignedWord getMinimumHeapSize() {
public final UnsignedWord getMinimumHeapSize() {
return sizes.minHeapSize;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
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
Expand All @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -790,7 +786,8 @@ private boolean isInAllocList(FreeListNode node) {
return false;
}

public UnsignedWord getReservedSpaceSize() {
@Override
public UnsignedWord getReservedAddressSpaceSize() {
return reservedSpaceSize;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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);
}

Expand Down
Loading
Loading