package com.oracle.svm.core.genscavenge;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.word.Word;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.SubstrateGCOptions;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.config.ConfigurationValues;
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.jdk.UninterruptibleUtils;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.option.XOptions;
import com.oracle.svm.core.thread.VMOperation;
import com.oracle.svm.core.util.UnsignedUtils;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
public final class HeapPolicy {
static final long LARGE_ARRAY_THRESHOLD_SENTINEL_VALUE = 0;
static final int ALIGNED_HEAP_CHUNK_FRACTION_FOR_LARGE_ARRAY_THRESHOLD = 8;
@Platforms(Platform.HOSTED_ONLY.class)
HeapPolicy() {
if (!SubstrateUtil.isPowerOf2(getAlignedHeapChunkSize().rawValue())) {
throw UserError.abort("AlignedHeapChunkSize (%d) should be a power of 2.", getAlignedHeapChunkSize().rawValue());
}
if (!getLargeArrayThreshold().belowOrEqual(getAlignedHeapChunkSize())) {
throw UserError.abort("LargeArrayThreshold (%d) should be below or equal to AlignedHeapChunkSize (%d).",
getLargeArrayThreshold().rawValue(), getAlignedHeapChunkSize().rawValue());
}
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static Word getProducedHeapChunkZapWord() {
return (Word) producedHeapChunkZapWord;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static int getProducedHeapChunkZapInt() {
return (int) producedHeapChunkZapInt.rawValue();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static Word getConsumedHeapChunkZapWord() {
return (Word) consumedHeapChunkZapWord;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static int getConsumedHeapChunkZapInt() {
return (int) consumedHeapChunkZapInt.rawValue();
}
public static UnsignedWord m(long bytes) {
assert 0 <= bytes;
return WordFactory.unsigned(bytes).multiply(1024).multiply(1024);
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static int getMaxSurvivorSpaces() {
return HeapPolicyOptions.MaxSurvivorSpaces.getValue();
}
private static UnsignedWord maximumYoungGenerationSize;
private static UnsignedWord minimumHeapSize;
private static UnsignedWord maximumHeapSize;
public static UnsignedWord getMaximumYoungGenerationSize() {
Log trace = Log.noopLog().string("[HeapPolicy.getMaximumYoungGenerationSize:");
if (maximumYoungGenerationSize.aboveThan(WordFactory.zero())) {
trace.string(" returns maximumYoungGenerationSize: ").unsigned(maximumYoungGenerationSize).string(" ]").newline();
return maximumYoungGenerationSize;
}
XOptions.XFlag xmn = XOptions.getXmn();
if (xmn.getEpoch() > 0) {
trace.string(" -Xmn.epoch: ").unsigned(xmn.getEpoch()).string(" -Xmn.value: ").unsigned(xmn.getValue());
setMaximumYoungGenerationSize(WordFactory.unsigned(xmn.getValue()));
trace.string(" returns: ").unsigned(maximumYoungGenerationSize)
.string(" ]").newline();
return maximumYoungGenerationSize;
}
long hostedValue = SubstrateGCOptions.MaxNewSize.getHostedValue();
if (hostedValue != 0) {
trace.string(" returns maximumYoungGenerationSize: ").unsigned(hostedValue).string(" ]").newline();
return WordFactory.unsigned(hostedValue);
}
UnsignedWord maxHeapSize = getMaximumHeapSize();
UnsignedWord youngSizeAsFraction = maxHeapSize.unsignedDivide(100).multiply(getMaximumYoungGenerationSizePercent());
UnsignedWord maxSize = m(256);
UnsignedWord youngSize = (youngSizeAsFraction.belowOrEqual(maxSize) ? youngSizeAsFraction : maxSize);
trace.string(" youngSize: ").unsigned(youngSize)
.string(" ]").newline();
return youngSize;
}
private static int getMaximumYoungGenerationSizePercent() {
int result = HeapPolicyOptions.MaximumYoungGenerationSizePercent.getValue();
VMError.guarantee((result >= 0) && (result <= 100), "MaximumYoungGenerationSizePercent should be in [0 ..100]");
return result;
}
public static UnsignedWord setMaximumYoungGenerationSize(UnsignedWord value) {
UnsignedWord result = maximumYoungGenerationSize;
maximumYoungGenerationSize = value;
return result;
}
public static UnsignedWord getMaximumHeapSize() {
if (maximumHeapSize.aboveThan(WordFactory.zero())) {
return maximumHeapSize;
}
XOptions.XFlag xmx = XOptions.getXmx();
if (xmx.getEpoch() > 0) {
HeapPolicy.setMaximumHeapSize(WordFactory.unsigned(xmx.getValue()));
return maximumHeapSize;
}
long hostedValue = SubstrateGCOptions.MaxHeapSize.getHostedValue();
if (hostedValue != 0) {
return WordFactory.unsigned(hostedValue);
}
UnsignedWord addressSpaceSize = getAddressSpaceSize();
if (PhysicalMemory.isInitialized()) {
UnsignedWord physicalMemorySize = PhysicalMemory.getCachedSize();
int maximumHeapSizePercent = getMaximumHeapSizePercent();
UnsignedWord result = physicalMemorySize.unsignedDivide(100).multiply(maximumHeapSizePercent);
if (result.belowThan(addressSpaceSize)) {
return result;
}
}
return addressSpaceSize;
}
private static UnsignedWord getAddressSpaceSize() {
int compressionShift = ReferenceAccess.singleton().getCompressEncoding().getShift();
if (compressionShift > 0) {
int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize();
return WordFactory.unsigned(1L << (referenceSize * Byte.SIZE)).shiftLeft(compressionShift);
}
return UnsignedUtils.MAX_VALUE;
}
private static int getMaximumHeapSizePercent() {
int result = HeapPolicyOptions.MaximumHeapSizePercent.getValue();
VMError.guarantee((result >= 0) && (result <= 100), "MaximumHeapSizePercent should be in [0 ..100]");
return result;
}
public static UnsignedWord setMaximumHeapSize(UnsignedWord value) {
Log trace = Log.noopLog().string("[HeapPolicy.setMaximumHeapSize:");
UnsignedWord result = maximumHeapSize;
maximumHeapSize = value;
trace.string(" old: ").unsigned(result).string(" new: ").unsigned(maximumHeapSize).string(" ]").newline();
return result;
}
public static UnsignedWord getMinimumHeapSize() {
Log trace = Log.noopLog().string("[HeapPolicy.getMinimumHeapSize:");
if (minimumHeapSize.aboveThan(WordFactory.zero())) {
trace.string(" returns: ").unsigned(minimumHeapSize).string(" ]").newline();
return minimumHeapSize;
}
XOptions.XFlag xms = XOptions.getXms();
if (xms.getEpoch() > 0) {
trace.string(" -Xms.epoch: ").unsigned(xms.getEpoch()).string(" -Xms.value: ").unsigned(xms.getValue());
setMinimumHeapSize(WordFactory.unsigned(xms.getValue()));
trace.string(" returns: ").unsigned(minimumHeapSize).string(" ]").newline();
return minimumHeapSize;
}
long hostedValue = SubstrateGCOptions.MinHeapSize.getHostedValue();
if (hostedValue != 0) {
trace.string(" returns: ").unsigned(hostedValue).string(" ]").newline();
return WordFactory.unsigned(hostedValue);
}
UnsignedWord result = getMaximumYoungGenerationSize().multiply(2);
if (result.aboveThan(getMaximumHeapSize())) {
result = getMaximumHeapSize();
}
trace.string(" returns: ").unsigned(result).string(" ]").newline();
return result;
}
public static UnsignedWord setMinimumHeapSize(UnsignedWord value) {
UnsignedWord result = minimumHeapSize;
minimumHeapSize = value;
return result;
}
@Fold
public static UnsignedWord getAlignedHeapChunkSize() {
return WordFactory.unsigned(HeapPolicyOptions.AlignedHeapChunkSize.getValue());
}
@Fold
static UnsignedWord getAlignedHeapChunkAlignment() {
return getAlignedHeapChunkSize();
}
@Fold
public static UnsignedWord getLargeArrayThreshold() {
long largeArrayThreshold = HeapPolicyOptions.LargeArrayThreshold.getValue();
if (LARGE_ARRAY_THRESHOLD_SENTINEL_VALUE == largeArrayThreshold) {
return getAlignedHeapChunkSize().unsignedDivide(ALIGNED_HEAP_CHUNK_FRACTION_FOR_LARGE_ARRAY_THRESHOLD);
} else {
return WordFactory.unsigned(HeapPolicyOptions.LargeArrayThreshold.getValue());
}
}
public static boolean getZapProducedHeapChunks() {
return HeapPolicyOptions.ZapChunks.getValue() || HeapPolicyOptions.ZapProducedHeapChunks.getValue();
}
public static boolean getZapConsumedHeapChunks() {
return HeapPolicyOptions.ZapChunks.getValue() || HeapPolicyOptions.ZapConsumedHeapChunks.getValue();
}
static {
Word.ensureInitialized();
}
private static final UnsignedWord producedHeapChunkZapInt = WordFactory.unsigned(0xbaadbeef);
private static final UnsignedWord producedHeapChunkZapWord = producedHeapChunkZapInt.shiftLeft(32).or(producedHeapChunkZapInt);
private static final UnsignedWord consumedHeapChunkZapInt = WordFactory.unsigned(0xdeadbeef);
private static final UnsignedWord consumedHeapChunkZapWord = consumedHeapChunkZapInt.shiftLeft(32).or(consumedHeapChunkZapInt);
private static final UninterruptibleUtils.AtomicUnsigned edenUsedBytes = new UninterruptibleUtils.AtomicUnsigned();
private static final UninterruptibleUtils.AtomicUnsigned youngUsedBytes = new UninterruptibleUtils.AtomicUnsigned();
public static void setEdenAndYoungGenBytes(UnsignedWord edenBytes, UnsignedWord youngBytes) {
assert VMOperation.isGCInProgress() : "would cause races otherwise";
youngUsedBytes.set(youngBytes);
edenUsedBytes.set(edenBytes);
}
public static void increaseEdenUsedBytes(UnsignedWord value) {
youngUsedBytes.addAndGet(value);
edenUsedBytes.addAndGet(value);
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getYoungUsedBytes() {
assert !VMOperation.isGCInProgress() : "value is incorrect during a GC";
return youngUsedBytes.get();
}
public static UnsignedWord getEdenUsedBytes() {
assert !VMOperation.isGCInProgress() : "value is incorrect during a GC";
return edenUsedBytes.get();
}
private static UnsignedWord getAllocationBeforePhysicalMemorySize() {
return WordFactory.unsigned(HeapPolicyOptions.AllocationBeforePhysicalMemorySize.getValue());
}
public static void maybeCollectOnAllocation() {
UnsignedWord maxYoungSize = getMaximumYoungGenerationSize();
maybeCollectOnAllocation(maxYoungSize);
}
@Uninterruptible(reason = "Avoid races with other threads that also try to trigger a GC")
private static void maybeCollectOnAllocation(UnsignedWord maxYoungSize) {
if (youngUsedBytes.get().aboveOrEqual(maxYoungSize)) {
GCImpl.getGCImpl().collectWithoutAllocating(GenScavengeGCCause.OnAllocation, false);
}
}
public static void maybeCauseUserRequestedCollection() {
if (!SubstrateGCOptions.DisableExplicitGC.getValue()) {
HeapImpl.getHeapImpl().getGC().collectCompletely(GCCause.JavaLangSystemGC);
}
}
public static final class TestingBackDoor {
private TestingBackDoor() {
}
public static long getUnalignedObjectSize() {
return HeapPolicy.getLargeArrayThreshold().rawValue();
}
}
static void samplePhysicalMemorySize() {
if (HeapImpl.getHeapImpl().getGCImpl().getCollectionEpoch().equal(WordFactory.zero()) &&
getYoungUsedBytes().aboveThan(getAllocationBeforePhysicalMemorySize())) {
PhysicalMemory.tryInitialize();
}
}
}