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 c62f182d3c20..9e1706d052d1 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 @@ -276,7 +276,7 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) { long optionMaxYoung = SubstrateGCOptions.MaxNewSize.getValue(); if (optionMaxYoung > 0L) { maxYoung = WordFactory.unsigned(optionMaxYoung); - } else if (HeapParameters.Options.MaximumYoungGenerationSizePercent.hasBeenSet()) { + } else if (GenScavengeGCOptions.MaximumYoungGenerationSizePercent.hasBeenSet()) { maxYoung = maxHeap.unsignedDivide(100).multiply(HeapParameters.getMaximumYoungGenerationSizePercent()); } else { maxYoung = maxHeap.unsignedDivide(AbstractCollectionPolicy.NEW_RATIO + 1); 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 29b6987bd642..6cec2ae1f568 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 @@ -24,11 +24,8 @@ */ package com.oracle.svm.core.genscavenge; -import static com.oracle.svm.core.genscavenge.BasicCollectionPolicies.Options.AllocationBeforePhysicalMemorySize; -import static com.oracle.svm.core.genscavenge.BasicCollectionPolicies.Options.PercentTimeInIncrementalCollection; import static com.oracle.svm.core.genscavenge.CollectionPolicy.shouldCollectYoungGenSeparately; -import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.UnsignedWord; @@ -38,21 +35,11 @@ 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.option.HostedOptionKey; -import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.util.TimeUtils; import com.oracle.svm.core.util.VMError; /** Basic/legacy garbage collection policies. */ final class BasicCollectionPolicies { - public static class Options { - @Option(help = "Percentage of total collection time that should be spent on young generation collections.")// - public static final RuntimeOptionKey PercentTimeInIncrementalCollection = new RuntimeOptionKey<>(50); - - @Option(help = "Bytes that can be allocated before (re-)querying the physical memory size") // - public static final HostedOptionKey AllocationBeforePhysicalMemorySize = new HostedOptionKey<>(1L * 1024L * 1024L); - } - @Platforms(Platform.HOSTED_ONLY.class) static int getMaxSurvivorSpaces(Integer userValue) { assert userValue == null || userValue >= 0; @@ -87,7 +74,7 @@ public void ensureSizeParametersInitialized() { @Override public void updateSizeParameters() { // Sample the physical memory size, before the first GC but after some allocation. - UnsignedWord allocationBeforeUpdate = WordFactory.unsigned(AllocationBeforePhysicalMemorySize.getValue()); + UnsignedWord allocationBeforeUpdate = WordFactory.unsigned(GenScavengeGCOptions.AllocationBeforePhysicalMemorySize.getValue()); if (GCImpl.getGCImpl().getCollectionEpoch().equal(WordFactory.zero()) && HeapImpl.getHeapImpl().getAccounting().getYoungUsedBytes().aboveOrEqual(allocationBeforeUpdate)) { PhysicalMemory.tryInitialize(); @@ -252,7 +239,7 @@ private UnsignedWord estimateUsedHeapAtNextIncrementalCollection() { } private static boolean enoughTimeSpentOnIncrementalGCs() { - int incrementalWeight = PercentTimeInIncrementalCollection.getValue(); + int incrementalWeight = SerialGCOptions.PercentTimeInIncrementalCollection.getValue(); assert incrementalWeight >= 0 && incrementalWeight <= 100 : "BySpaceAndTimePercentTimeInIncrementalCollection should be in the range [0..100]."; GCAccounting accounting = GCImpl.getGCImpl().getAccounting(); 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 581ea291de19..0d667cb108e6 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 org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.UnsignedWord; @@ -32,16 +31,10 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.heap.PhysicalMemory; -import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.util.UserError; /** The interface for a garbage collection policy. All sizes are in bytes. */ public interface CollectionPolicy { - final class Options { - @Option(help = "The garbage collection policy, either Adaptive (default) or BySpaceAndTime.")// - public static final HostedOptionKey InitialCollectionPolicy = new HostedOptionKey<>("Adaptive"); - } - @Platforms(Platform.HOSTED_ONLY.class) static String getInitialPolicyName() { if (SubstrateOptions.UseEpsilonGC.getValue()) { @@ -49,7 +42,7 @@ static String getInitialPolicyName() { } else if (!SubstrateOptions.useRememberedSet()) { return "OnlyCompletely"; } - String name = Options.InitialCollectionPolicy.getValue(); + String name = SerialGCOptions.InitialCollectionPolicy.getValue(); String legacyPrefix = "com.oracle.svm.core.genscavenge.CollectionPolicy$"; if (name.startsWith(legacyPrefix)) { return name.substring(legacyPrefix.length()); @@ -87,7 +80,7 @@ static int getMaxSurvivorSpaces(Integer userValue) { } static boolean shouldCollectYoungGenSeparately(boolean defaultValue) { - Boolean optionValue = HeapParameters.Options.CollectYoungGenerationSeparately.getValue(); + Boolean optionValue = SerialGCOptions.CollectYoungGenerationSeparately.getValue(); return (optionValue != null) ? optionValue : defaultValue; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCAccounting.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCAccounting.java index 7b312f0fc325..c6cb4641a535 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCAccounting.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCAccounting.java @@ -131,7 +131,7 @@ void beforeCollection(boolean completeCollection) { oldChunkBytesBefore = oldSpace.getChunkBytes(); /* Objects are allocated in the young generation. */ allocatedChunkBytes = allocatedChunkBytes.add(youngGen.getEden().getChunkBytes()); - if (HeapOptions.PrintGCSummary.getValue()) { + if (SerialGCOptions.PrintGCSummary.getValue()) { UnsignedWord edenObjectBytesBefore = youngGen.getEden().computeObjectBytes(); youngObjectBytesBefore = edenObjectBytesBefore.add(youngGen.computeSurvivorObjectBytes()); oldObjectBytesBefore = oldSpace.computeObjectBytes(); @@ -197,7 +197,7 @@ private void afterCollectionCommon() { UnsignedWord afterChunkBytes = oldChunkBytesAfter.add(youngChunkBytesAfter); UnsignedWord collectedChunkBytes = beforeChunkBytes.subtract(afterChunkBytes); collectedTotalChunkBytes = collectedTotalChunkBytes.add(collectedChunkBytes); - if (HeapOptions.PrintGCSummary.getValue()) { + if (SerialGCOptions.PrintGCSummary.getValue()) { UnsignedWord youngObjectBytesAfter = youngGen.computeObjectBytes(); UnsignedWord oldObjectBytesAfter = oldSpace.computeObjectBytes(); UnsignedWord beforeObjectBytes = youngObjectBytesBefore.add(oldObjectBytesBefore); 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 d2f485f4de55..12ff9fecc1a6 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 @@ -357,7 +357,7 @@ public static UnsignedWord getChunkBytes() { private void printGCBefore(String cause) { Log verboseGCLog = Log.log(); HeapImpl heap = HeapImpl.getHeapImpl(); - sizeBefore = ((SubstrateGCOptions.PrintGC.getValue() || HeapOptions.PrintHeapShape.getValue()) ? getChunkBytes() : WordFactory.zero()); + sizeBefore = ((SubstrateGCOptions.PrintGC.getValue() || SerialGCOptions.PrintHeapShape.getValue()) ? getChunkBytes() : WordFactory.zero()); if (SubstrateGCOptions.VerboseGC.getValue() && getCollectionEpoch().equal(1)) { verboseGCLog.string("[Heap policy parameters: ").newline(); verboseGCLog.string(" YoungGenerationSize: ").unsigned(getPolicy().getMaximumYoungGenerationSize()).newline(); @@ -365,7 +365,7 @@ private void printGCBefore(String cause) { verboseGCLog.string(" MinimumHeapSize: ").unsigned(getPolicy().getMinimumHeapSize()).newline(); verboseGCLog.string(" AlignedChunkSize: ").unsigned(HeapParameters.getAlignedHeapChunkSize()).newline(); verboseGCLog.string(" LargeArrayThreshold: ").unsigned(HeapParameters.getLargeArrayThreshold()).string("]").newline(); - if (HeapOptions.PrintHeapShape.getValue()) { + if (SerialGCOptions.PrintHeapShape.getValue()) { HeapImpl.getHeapImpl().logImageHeapPartitionBoundaries(verboseGCLog); } } @@ -373,13 +373,13 @@ private void printGCBefore(String cause) { verboseGCLog.string("["); verboseGCLog.string("["); long startTime = System.nanoTime(); - if (HeapOptions.PrintGCTimeStamps.getValue()) { + if (SerialGCOptions.PrintGCTimeStamps.getValue()) { verboseGCLog.unsigned(TimeUtils.roundNanosToMillis(Timer.getTimeSinceFirstAllocation(startTime))).string(" msec: "); } else { verboseGCLog.unsigned(startTime); } verboseGCLog.string(" GC:").string(" before").string(" epoch: ").unsigned(getCollectionEpoch()).string(" cause: ").string(cause); - if (HeapOptions.PrintHeapShape.getValue()) { + if (SerialGCOptions.PrintHeapShape.getValue()) { heap.report(verboseGCLog); } verboseGCLog.string("]").newline(); @@ -394,7 +394,7 @@ private void printGCAfter(String cause) { Log printGCLog = Log.log(); UnsignedWord sizeAfter = getChunkBytes(); printGCLog.string("["); - if (HeapOptions.PrintGCTimeStamps.getValue()) { + if (SerialGCOptions.PrintGCTimeStamps.getValue()) { long finishNanos = timers.collection.getClosedTime(); printGCLog.unsigned(TimeUtils.roundNanosToMillis(Timer.getTimeSinceFirstAllocation(finishNanos))).string(" msec: "); } @@ -409,7 +409,7 @@ private void printGCAfter(String cause) { if (SubstrateGCOptions.VerboseGC.getValue()) { verboseGCLog.string(" ["); long finishNanos = timers.collection.getClosedTime(); - if (HeapOptions.PrintGCTimeStamps.getValue()) { + if (SerialGCOptions.PrintGCTimeStamps.getValue()) { verboseGCLog.unsigned(TimeUtils.roundNanosToMillis(Timer.getTimeSinceFirstAllocation(finishNanos))).string(" msec: "); } else { verboseGCLog.unsigned(finishNanos); @@ -418,10 +418,10 @@ private void printGCAfter(String cause) { verboseGCLog.string(" policy: "); verboseGCLog.string(getPolicy().getName()); verboseGCLog.string(" type: ").string(completeCollection ? "complete" : "incremental"); - if (HeapOptions.PrintHeapShape.getValue()) { + if (SerialGCOptions.PrintHeapShape.getValue()) { heap.report(verboseGCLog); } - if (!HeapOptions.PrintGCTimes.getValue()) { + if (!SerialGCOptions.PrintGCTimes.getValue()) { verboseGCLog.newline(); verboseGCLog.string(" collection time: ").unsigned(timers.collection.getMeasuredNanos()).string(" nanoSeconds"); } else { @@ -1167,7 +1167,7 @@ static void doReferenceHandling() { long startTime = System.nanoTime(); ReferenceHandler.processPendingReferencesInRegularThread(); - if (SubstrateGCOptions.VerboseGC.getValue() && HeapOptions.PrintGCTimes.getValue()) { + if (SubstrateGCOptions.VerboseGC.getValue() && SerialGCOptions.PrintGCTimes.getValue()) { long executionTime = System.nanoTime() - startTime; Log.log().string("[GC epilogue reference processing and cleaners: ").signed(executionTime).string("]").newline(); } @@ -1349,7 +1349,7 @@ private static > T getLast(T chunks) { } private void printGCSummary() { - if (!HeapOptions.PrintGCSummary.getValue()) { + if (!SerialGCOptions.PrintGCSummary.getValue()) { return; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeGCOptions.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeGCOptions.java new file mode 100644 index 000000000000..7016f89b1467 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeGCOptions.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022, 2022, 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.genscavenge; + +import com.oracle.svm.core.SubstrateOptions; +import org.graalvm.collections.EconomicMap; +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.compiler.options.OptionType; + +import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.option.NotifyGCRuntimeOptionKey; +import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.option.RuntimeOptionKey; +import com.oracle.svm.core.util.InterruptImageBuilding; +import com.oracle.svm.core.util.UserError; + +/** Common options that can be specified for both the serial and the epsilon GC. */ +public final class GenScavengeGCOptions { + @Option(help = "Serial and epsilon GC only: the maximum heap size as percent of physical memory.", type = OptionType.User) // + public static final RuntimeOptionKey MaximumHeapSizePercent = new NotifyGCRuntimeOptionKey<>(80, GenScavengeGCOptions::serialOrEpsilonGCOnly); + + @Option(help = "Serial and epsilon GC only: the maximum size of the young generation as a percentage of the maximum heap size.", type = OptionType.User) // + public static final RuntimeOptionKey MaximumYoungGenerationSizePercent = new NotifyGCRuntimeOptionKey<>(10, GenScavengeGCOptions::serialOrEpsilonGCOnly); + + @Option(help = "Serial and epsilon GC only: the size of an aligned chunk.", type = OptionType.Expert) // + public static final HostedOptionKey AlignedHeapChunkSize = new HostedOptionKey<>(1L * 1024L * 1024L, GenScavengeGCOptions::serialOrEpsilonGCOnly) { + @Override + protected void onValueUpdate(EconomicMap, Object> values, Long oldValue, Long newValue) { + int multiple = 4096; + UserError.guarantee(newValue > 0 && newValue % multiple == 0, "%s value must be a multiple of %d.", getName(), multiple); + } + }; + + /* + * This should be a fraction of the size of an aligned chunk, else large small arrays will not + * fit in an aligned chunk. + */ + @Option(help = "Serial and epsilon GC only: the size at or above which an array will be allocated in its own unaligned chunk. 0 implies (AlignedHeapChunkSize / 8).", type = OptionType.Expert) // + public static final HostedOptionKey LargeArrayThreshold = new HostedOptionKey<>(0L, GenScavengeGCOptions::serialOrEpsilonGCOnly); + + @Option(help = "Serial and epsilon GC only: fill unused memory chunks with a sentinel value.", type = OptionType.Debug) // + public static final HostedOptionKey ZapChunks = new HostedOptionKey<>(false, GenScavengeGCOptions::serialOrEpsilonGCOnly); + + @Option(help = "Serial and epsilon GC only: before use, fill memory chunks with a sentinel value.", type = OptionType.Debug) // + public static final HostedOptionKey ZapProducedHeapChunks = new HostedOptionKey<>(false, GenScavengeGCOptions::serialOrEpsilonGCOnly); + + @Option(help = "Serial and epsilon GC only: after use, Fill memory chunks with a sentinel value.", type = OptionType.Debug) // + public static final HostedOptionKey ZapConsumedHeapChunks = new HostedOptionKey<>(false, GenScavengeGCOptions::serialOrEpsilonGCOnly); + + @Option(help = "Serial and epsilon GC only: bytes that can be allocated before (re-)querying the physical memory size", type = OptionType.Debug) // + public static final HostedOptionKey AllocationBeforePhysicalMemorySize = new HostedOptionKey<>(1L * 1024L * 1024L, GenScavengeGCOptions::serialOrEpsilonGCOnly); + + @Option(help = "Serial and epsilon GC only: number of bytes at the beginning of each heap chunk that are not used for payload data, i.e., can be freely used as metadata by the heap chunk provider.", type = OptionType.Debug) // + public static final HostedOptionKey HeapChunkHeaderPadding = new HostedOptionKey<>(0, GenScavengeGCOptions::serialOrEpsilonGCOnly); + + private GenScavengeGCOptions() { + } + + private static void serialOrEpsilonGCOnly(OptionKey optionKey) { + if (!SubstrateOptions.UseSerialGC.getValue() && !SubstrateOptions.UseEpsilonGC.getValue()) { + throw new InterruptImageBuilding("The option " + optionKey.getName() + " is garbage collector specific and cannot be specified if the " + + Heap.getHeap().getGC().getName() + " is used at runtime."); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjRefVisitor.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjRefVisitor.java index c5cc8d9dab36..95205bc7dc98 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjRefVisitor.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjRefVisitor.java @@ -24,7 +24,6 @@ */ package com.oracle.svm.core.genscavenge; -import org.graalvm.compiler.options.Option; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -37,7 +36,6 @@ import com.oracle.svm.core.heap.ReferenceAccess; import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.log.Log; -import com.oracle.svm.core.option.HostedOptionKey; /** * This visitor is handed Pointers to Object references and if necessary it promotes the @@ -53,7 +51,7 @@ final class GreyToBlackObjRefVisitor implements ObjectReferenceVisitor { @Platforms(Platform.HOSTED_ONLY.class) GreyToBlackObjRefVisitor() { - if (Options.GreyToBlackObjRefDemographics.getValue()) { + if (SerialGCOptions.GreyToBlackObjRefDemographics.getValue()) { counters = new RealCounters(); } else { counters = new NoopCounters(); @@ -125,11 +123,6 @@ public Counters openCounters() { return counters.open(); } - public static class Options { - @Option(help = "Develop demographics of the object references visited.")// - public static final HostedOptionKey GreyToBlackObjRefDemographics = new HostedOptionKey<>(false); - } - public interface Counters extends AutoCloseable { Counters open(); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunk.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunk.java index 0055d2d28982..333ab3b4d515 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunk.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunk.java @@ -26,7 +26,6 @@ import java.util.function.IntUnaryOperator; -import org.graalvm.compiler.options.Option; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -47,7 +46,6 @@ import com.oracle.svm.core.c.struct.PinnedObjectField; import com.oracle.svm.core.heap.ObjectVisitor; import com.oracle.svm.core.hub.LayoutEncoding; -import com.oracle.svm.core.option.HostedOptionKey; /** * The common structure of the chunks of memory which make up the heap. HeapChunks are aggregated @@ -82,16 +80,11 @@ public final class HeapChunk { private HeapChunk() { // all static } - static class Options { - @Option(help = "Number of bytes at the beginning of each heap chunk that are not used for payload data, i.e., can be freely used as metadata by the heap chunk provider.") // - public static final HostedOptionKey HeapChunkHeaderPadding = new HostedOptionKey<>(0); - } - static class HeaderPaddingSizeProvider implements IntUnaryOperator { @Override public int applyAsInt(int operand) { assert operand == 0 : "padding structure does not declare any fields"; - return Options.HeapChunkHeaderPadding.getValue(); + return GenScavengeGCOptions.HeapChunkHeaderPadding.getValue(); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkProvider.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkProvider.java index b13050544fcf..1484146c83ae 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkProvider.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapChunkProvider.java @@ -148,7 +148,7 @@ void consumeAlignedChunks(AlignedHeader firstChunk, boolean keepAll) { } else { UnsignedWord freeListBytes = getBytesInUnusedChunks(); UnsignedWord reserveBytes = GCImpl.getPolicy().getMaximumFreeAlignedChunksSize(); - UnsignedWord maxHeapFree = WordFactory.unsigned(HeapParameters.Options.MaxHeapFree.getValue()); + UnsignedWord maxHeapFree = WordFactory.unsigned(SerialGCOptions.MaxHeapFree.getValue()); if (maxHeapFree.aboveThan(0)) { reserveBytes = UnsignedUtils.min(reserveBytes, maxHeapFree); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java index ee9122396a67..0b2fc34b44b5 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java @@ -278,7 +278,7 @@ public UnsignedWord getCommittedBytes() { } void report(Log log) { - report(log, HeapParameters.Options.TraceHeapChunks.getValue()); + report(log, SerialGCOptions.TraceHeapChunks.getValue()); } void report(Log log, boolean traceHeapChunks) { @@ -400,7 +400,7 @@ public static boolean usesImageHeapChunks() { @Fold public static boolean usesImageHeapCardMarking() { - Boolean enabled = HeapOptions.ImageHeapCardMarking.getValue(); + Boolean enabled = SerialGCOptions.ImageHeapCardMarking.getValue(); if (enabled == Boolean.FALSE || enabled == null && !SubstrateOptions.useRememberedSet()) { return false; } else if (enabled == null) { @@ -408,7 +408,7 @@ public static boolean usesImageHeapCardMarking() { } UserError.guarantee(CommittedMemoryProvider.get().guaranteesHeapPreferredAddressSpaceAlignment(), "Enabling option %s requires a custom image heap alignment at runtime, which cannot be ensured with the current configuration (option %s might be disabled)", - HeapOptions.ImageHeapCardMarking, SubstrateOptions.SpawnIsolates); + SerialGCOptions.ImageHeapCardMarking, SubstrateOptions.SpawnIsolates); return true; } @@ -427,7 +427,7 @@ public int getImageHeapOffsetInAddressSpace() { * the heap base and the start of the image heap. The gap won't need any memory in the * native image file. */ - return NumUtil.safeToInt(HeapParameters.Options.AlignedHeapChunkSize.getValue()); + return NumUtil.safeToInt(GenScavengeGCOptions.AlignedHeapChunkSize.getValue()); } return 0; } @@ -886,7 +886,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev } } -@TargetClass(value = java.lang.Runtime.class, onlyWith = UseSerialGC.class) +@TargetClass(value = java.lang.Runtime.class, onlyWith = UseSerialOrEpsilonGC.class) @SuppressWarnings("static-method") final class Target_java_lang_Runtime { @Substitute diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapOptions.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapOptions.java deleted file mode 100644 index 4c96ce9c865f..000000000000 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapOptions.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2017, 2017, 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.genscavenge; - -import org.graalvm.compiler.options.Option; -import org.graalvm.compiler.options.OptionType; - -import com.oracle.svm.core.option.HostedOptionKey; -import com.oracle.svm.core.option.RuntimeOptionKey; - -public final class HeapOptions { - @Option(help = "Print the shape of the heap before and after each collection, if +VerboseGC.")// - public static final RuntimeOptionKey PrintHeapShape = new RuntimeOptionKey<>(false); - - @Option(help = "Print summary GC information after application main method returns.")// - public static final RuntimeOptionKey PrintGCSummary = new RuntimeOptionKey<>(false); - - @Option(help = "Print a time stamp at each collection, if +PrintGC or +VerboseGC.")// - public static final RuntimeOptionKey PrintGCTimeStamps = new RuntimeOptionKey<>(false); - - @Option(help = "Print the time for each of the phases of each collection, if +VerboseGC.")// - public static final RuntimeOptionKey PrintGCTimes = new RuntimeOptionKey<>(false); - - @Option(help = "Verify the remembered set if VerifyHeap is enabled.")// - public static final HostedOptionKey VerifyRememberedSet = new HostedOptionKey<>(true); - - @Option(help = "Verify all object references if VerifyHeap is enabled.")// - public static final HostedOptionKey VerifyReferences = new HostedOptionKey<>(true); - - @Option(help = "Soft references: this number of milliseconds multiplied by the free heap memory in MByte is the time span " + - "for which a soft reference will keep its referent alive after its last access.", type = OptionType.Expert) // - public static final HostedOptionKey SoftRefLRUPolicyMSPerMB = new HostedOptionKey<>(1000); - - @Option(help = "Enables card marking for image heap objects, which arranges them in chunks. Automatically enabled when supported.", type = OptionType.Expert) // - public static final HostedOptionKey ImageHeapCardMarking = new HostedOptionKey<>(null); - - private HeapOptions() { - } -} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapParameters.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapParameters.java index 4e7082ad048b..0625bcf4b254 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapParameters.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapParameters.java @@ -24,13 +24,7 @@ */ package com.oracle.svm.core.genscavenge; -import org.graalvm.collections.EconomicMap; -import org.graalvm.collections.UnmodifiableEconomicMap; import org.graalvm.compiler.api.replacements.Fold; -import org.graalvm.compiler.options.Option; -import org.graalvm.compiler.options.OptionKey; -import org.graalvm.compiler.options.OptionType; -import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -40,76 +34,11 @@ import com.oracle.svm.core.SubstrateGCOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Uninterruptible; -import com.oracle.svm.core.option.GCRuntimeOptionKey; -import com.oracle.svm.core.option.HostedOptionKey; -import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; /** Constants and variables for the size and layout of the heap and behavior of the collector. */ public final class HeapParameters { - public static final class Options { - @Option(help = "The maximum heap size as percent of physical memory") // - public static final RuntimeOptionKey MaximumHeapSizePercent = new GCRuntimeOptionKey<>(80); - - @Option(help = "The maximum size of the young generation as a percentage of the maximum heap size") // - public static final RuntimeOptionKey MaximumYoungGenerationSizePercent = new GCRuntimeOptionKey<>(10); - - @Option(help = "The size of an aligned chunk.") // - public static final HostedOptionKey AlignedHeapChunkSize = new HostedOptionKey<>(1L * 1024L * 1024L) { - @Override - protected void onValueUpdate(EconomicMap, Object> values, Long oldValue, Long newValue) { - int multiple = 4096; - UserError.guarantee(newValue > 0 && newValue % multiple == 0, "%s value must be a multiple of %d.", getName(), multiple); - } - }; - - /* - * This should be a fraction of the size of an aligned chunk, else large small arrays will - * not fit in an aligned chunk. - */ - @Option(help = "The size at or above which an array will be allocated in its own unaligned chunk. 0 implies (AlignedHeapChunkSize / 8).") // - public static final HostedOptionKey LargeArrayThreshold = new HostedOptionKey<>(LARGE_ARRAY_THRESHOLD_SENTINEL_VALUE); - - @Option(help = "Fill unused memory chunks with a sentinel value.") // - public static final HostedOptionKey ZapChunks = new HostedOptionKey<>(false); - - @Option(help = "Before use, fill memory chunks with a sentinel value.") // - public static final HostedOptionKey ZapProducedHeapChunks = new HostedOptionKey<>(false); - - @Option(help = "After use, Fill memory chunks with a sentinel value.") // - public static final HostedOptionKey ZapConsumedHeapChunks = new HostedOptionKey<>(false); - - @Option(help = "Trace heap chunks during collections, if +VerboseGC and +PrintHeapShape.") // - public static final RuntimeOptionKey TraceHeapChunks = new RuntimeOptionKey<>(false); - - @Option(help = "Maximum number of survivor spaces.") // - public static final HostedOptionKey MaxSurvivorSpaces = new HostedOptionKey<>(null) { - @Override - public Integer getValueOrDefault(UnmodifiableEconomicMap, Object> values) { - Integer value = (Integer) values.get(this); - UserError.guarantee(value == null || value >= 0, "%s value must be greater than or equal to 0", getName()); - return CollectionPolicy.getMaxSurvivorSpaces(value); - } - - @Override - public Integer getValue(OptionValues values) { - assert checkDescriptorExists(); - return getValueOrDefault(values.getMap()); - } - }; - - @Option(help = "Determines if a full GC collects the young generation separately or together with the old generation.") // - public static final RuntimeOptionKey CollectYoungGenerationSeparately = new RuntimeOptionKey<>(null); - - @Option(help = "The maximum free bytes reserved for allocations, in bytes (0 for automatic according to GC policy).", type = OptionType.User)// - public static final RuntimeOptionKey MaxHeapFree = new RuntimeOptionKey<>(0L); - - private Options() { - } - } - - private static final long LARGE_ARRAY_THRESHOLD_SENTINEL_VALUE = 0; private static final int ALIGNED_HEAP_CHUNK_FRACTION_FOR_LARGE_ARRAY_THRESHOLD = 8; @Platforms(Platform.HOSTED_ONLY.class) @@ -145,7 +74,7 @@ public static int getConsumedHeapChunkZapInt() { @Fold public static int getMaxSurvivorSpaces() { - return Options.MaxSurvivorSpaces.getValue(); + return SerialGCOptions.MaxSurvivorSpaces.getValue(); } /* @@ -161,28 +90,28 @@ public static void setMinimumHeapSize(UnsignedWord value) { } public static void setMaximumHeapFree(UnsignedWord bytes) { - HeapParameters.Options.MaxHeapFree.update(bytes.rawValue()); + SerialGCOptions.MaxHeapFree.update(bytes.rawValue()); } public static UnsignedWord getMaximumHeapFree() { - return WordFactory.unsigned(HeapParameters.Options.MaxHeapFree.getValue()); + return WordFactory.unsigned(SerialGCOptions.MaxHeapFree.getValue()); } static int getMaximumYoungGenerationSizePercent() { - int result = Options.MaximumYoungGenerationSizePercent.getValue(); + int result = GenScavengeGCOptions.MaximumYoungGenerationSizePercent.getValue(); VMError.guarantee((result >= 0) && (result <= 100), "MaximumYoungGenerationSizePercent should be in [0 ..100]"); return result; } static int getMaximumHeapSizePercent() { - int result = Options.MaximumHeapSizePercent.getValue(); + int result = GenScavengeGCOptions.MaximumHeapSizePercent.getValue(); VMError.guarantee((result >= 0) && (result <= 100), "MaximumHeapSizePercent should be in [0 ..100]"); return result; } @Fold public static UnsignedWord getAlignedHeapChunkSize() { - return WordFactory.unsigned(Options.AlignedHeapChunkSize.getValue()); + return WordFactory.unsigned(GenScavengeGCOptions.AlignedHeapChunkSize.getValue()); } @Fold @@ -192,11 +121,11 @@ static UnsignedWord getAlignedHeapChunkAlignment() { @Fold public static UnsignedWord getLargeArrayThreshold() { - long largeArrayThreshold = Options.LargeArrayThreshold.getValue(); - if (LARGE_ARRAY_THRESHOLD_SENTINEL_VALUE == largeArrayThreshold) { + long largeArrayThreshold = GenScavengeGCOptions.LargeArrayThreshold.getValue(); + if (largeArrayThreshold == 0) { return getAlignedHeapChunkSize().unsignedDivide(ALIGNED_HEAP_CHUNK_FRACTION_FOR_LARGE_ARRAY_THRESHOLD); } else { - return WordFactory.unsigned(Options.LargeArrayThreshold.getValue()); + return WordFactory.unsigned(GenScavengeGCOptions.LargeArrayThreshold.getValue()); } } @@ -205,11 +134,11 @@ public static UnsignedWord getLargeArrayThreshold() { */ public static boolean getZapProducedHeapChunks() { - return Options.ZapChunks.getValue() || Options.ZapProducedHeapChunks.getValue(); + return GenScavengeGCOptions.ZapChunks.getValue() || GenScavengeGCOptions.ZapProducedHeapChunks.getValue(); } public static boolean getZapConsumedHeapChunks() { - return Options.ZapChunks.getValue() || Options.ZapConsumedHeapChunks.getValue(); + return GenScavengeGCOptions.ZapChunks.getValue() || GenScavengeGCOptions.ZapConsumedHeapChunks.getValue(); } static { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifier.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifier.java index 78f8cb73c4a5..20e3678afd23 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifier.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifier.java @@ -142,7 +142,7 @@ private static boolean verifyRememberedSets() { * reasonable state. Now, we can verify the remembered sets without having to worry about * basic heap consistency. */ - if (!SubstrateOptions.useRememberedSet() || !HeapOptions.VerifyRememberedSet.getValue()) { + if (!SubstrateOptions.useRememberedSet() || !SerialGCOptions.VerifyRememberedSet.getValue()) { return true; } @@ -326,7 +326,7 @@ private static boolean verifyObject(Object obj, AlignedHeader aChunk, UnalignedH // This method is executed exactly once per object in the heap. private static boolean verifyReferences(Object obj) { - if (!HeapOptions.VerifyReferences.getValue()) { + if (!SerialGCOptions.VerifyReferences.getValue()) { return true; } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ReferenceObjectProcessing.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ReferenceObjectProcessing.java index b4bc60f18ae1..3c0a614336fb 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ReferenceObjectProcessing.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ReferenceObjectProcessing.java @@ -182,7 +182,7 @@ static Reference processRememberedReferences() { static void afterCollection(UnsignedWord freeBytes) { assert rememberedRefsList == null; UnsignedWord unused = freeBytes.unsignedDivide(1024 * 1024 /* MB */); - maxSoftRefAccessIntervalMs = unused.multiply(HeapOptions.SoftRefLRUPolicyMSPerMB.getValue()); + maxSoftRefAccessIntervalMs = unused.multiply(SerialGCOptions.SoftRefLRUPolicyMSPerMB.getValue()); ReferenceInternals.updateSoftReferenceClock(); if (initialSoftRefClock == 0) { initialSoftRefClock = ReferenceInternals.getSoftReferenceClock(); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialGCOptions.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialGCOptions.java new file mode 100644 index 000000000000..b9fb2903ea8f --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialGCOptions.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022, 2022, 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.genscavenge; + +import org.graalvm.collections.UnmodifiableEconomicMap; +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.compiler.options.OptionType; +import org.graalvm.compiler.options.OptionValues; + +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.option.RuntimeOptionKey; +import com.oracle.svm.core.util.InterruptImageBuilding; +import com.oracle.svm.core.util.UserError; + +/** Options that are only valid for the serial GC (and not for the epsilon GC). */ +public final class SerialGCOptions { + @Option(help = "Serial GC only: the garbage collection policy, either Adaptive (default) or BySpaceAndTime.", type = OptionType.User)// + public static final HostedOptionKey InitialCollectionPolicy = new HostedOptionKey<>("Adaptive", SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: percentage of total collection time that should be spent on young generation collections.", type = OptionType.User)// + public static final RuntimeOptionKey PercentTimeInIncrementalCollection = new RuntimeOptionKey<>(50, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: the maximum free bytes reserved for allocations, in bytes (0 for automatic according to GC policy).", type = OptionType.User)// + public static final RuntimeOptionKey MaxHeapFree = new RuntimeOptionKey<>(0L, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: maximum number of survivor spaces.", type = OptionType.Expert) // + public static final HostedOptionKey MaxSurvivorSpaces = new HostedOptionKey<>(null, SerialGCOptions::serialGCOnly) { + @Override + public Integer getValueOrDefault(UnmodifiableEconomicMap, Object> values) { + Integer value = (Integer) values.get(this); + UserError.guarantee(value == null || value >= 0, "%s value must be greater than or equal to 0", getName()); + return CollectionPolicy.getMaxSurvivorSpaces(value); + } + + @Override + public Integer getValue(OptionValues values) { + assert checkDescriptorExists(); + return getValueOrDefault(values.getMap()); + } + }; + + @Option(help = "Serial GC only: determines if a full GC collects the young generation separately or together with the old generation.", type = OptionType.Expert) // + public static final RuntimeOptionKey CollectYoungGenerationSeparately = new RuntimeOptionKey<>(null, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: enables card marking for image heap objects, which arranges them in chunks. Automatically enabled when supported.", type = OptionType.Expert) // + public static final HostedOptionKey ImageHeapCardMarking = new HostedOptionKey<>(null, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: this number of milliseconds multiplied by the free heap memory in MByte is the time span " + + "for which a soft reference will keep its referent alive after its last access.", type = OptionType.Expert) // + public static final HostedOptionKey SoftRefLRUPolicyMSPerMB = new HostedOptionKey<>(1000, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: print the shape of the heap before and after each collection, if +VerboseGC.", type = OptionType.Debug)// + public static final RuntimeOptionKey PrintHeapShape = new RuntimeOptionKey<>(false, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: print summary GC information after application main method returns.", type = OptionType.Debug)// + public static final RuntimeOptionKey PrintGCSummary = new RuntimeOptionKey<>(false, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: print a time stamp at each collection, if +PrintGC or +VerboseGC.", type = OptionType.Debug)// + public static final RuntimeOptionKey PrintGCTimeStamps = new RuntimeOptionKey<>(false, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: print the time for each of the phases of each collection, if +VerboseGC.", type = OptionType.Debug)// + public static final RuntimeOptionKey PrintGCTimes = new RuntimeOptionKey<>(false, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: instrument write barriers with counters", type = OptionType.Debug)// + public static final HostedOptionKey CountWriteBarriers = new HostedOptionKey<>(false, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: verify the remembered set if VerifyHeap is enabled.", type = OptionType.Debug)// + public static final HostedOptionKey VerifyRememberedSet = new HostedOptionKey<>(true, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: verify all object references if VerifyHeap is enabled.", type = OptionType.Debug)// + public static final HostedOptionKey VerifyReferences = new HostedOptionKey<>(true, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: verify write barriers", type = OptionType.Debug)// + public static final HostedOptionKey VerifyWriteBarriers = new HostedOptionKey<>(false, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: trace heap chunks during collections, if +VerboseGC and +PrintHeapShape.", type = OptionType.Debug) // + public static final RuntimeOptionKey TraceHeapChunks = new RuntimeOptionKey<>(false, SerialGCOptions::serialGCOnly); + + @Option(help = "Serial GC only: develop demographics of the object references visited.", type = OptionType.Debug)// + public static final HostedOptionKey GreyToBlackObjRefDemographics = new HostedOptionKey<>(false, SerialGCOptions::serialGCOnly); + + private SerialGCOptions() { + } + + private static void serialGCOnly(OptionKey optionKey) { + if (!SubstrateOptions.UseSerialGC.getValue() && !SubstrateOptions.UseEpsilonGC.getValue()) { + throw new InterruptImageBuilding("The option " + optionKey.getName() + " is garbage collector specific and cannot be specified if the " + + Heap.getHeap().getGC().getName() + " is used at runtime."); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UseSerialGC.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UseSerialGC.java index 0d49f2dc6f0e..6120da36b0bc 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UseSerialGC.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UseSerialGC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, 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 @@ -33,11 +33,8 @@ @Platforms(Platform.HOSTED_ONLY.class) class UseSerialGC implements BooleanSupplier { - UseSerialGC() { - } - @Override public boolean getAsBoolean() { - return SubstrateOptions.UseSerialGC.getValue(); + return SubstrateOptions.UseSerialGC.getValue() && !SubstrateOptions.UseEpsilonGC.getValue(); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UseSerialOrEpsilonGC.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UseSerialOrEpsilonGC.java new file mode 100644 index 000000000000..cce0bb85bf71 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/UseSerialOrEpsilonGC.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019, 2019, 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.genscavenge; + +import java.util.function.BooleanSupplier; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.SubstrateOptions; + +@Platforms(Platform.HOSTED_ONLY.class) +class UseSerialOrEpsilonGC implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return SubstrateOptions.UseSerialGC.getValue() || SubstrateOptions.UseEpsilonGC.getValue(); + } +} diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/BarrierSnippets.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/BarrierSnippets.java index 8f00eac2ee49..3ef159149fb6 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/BarrierSnippets.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/BarrierSnippets.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; +import com.oracle.svm.core.genscavenge.SerialGCOptions; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.api.replacements.Snippet; import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter; @@ -42,7 +43,6 @@ import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode; import org.graalvm.compiler.nodes.spi.LoweringTool; import org.graalvm.compiler.nodes.type.StampTool; -import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.replacements.SnippetTemplate; @@ -61,7 +61,6 @@ import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; import com.oracle.svm.core.graal.snippets.SubstrateTemplates; import com.oracle.svm.core.heap.StoredContinuation; -import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.util.Counter; import com.oracle.svm.core.util.CounterFeature; @@ -71,14 +70,6 @@ public class BarrierSnippets extends SubstrateTemplates implements Snippets { /** A LocationIdentity to distinguish card locations from other locations. */ public static final LocationIdentity CARD_REMEMBERED_SET_LOCATION = NamedLocationIdentity.mutable("CardRememberedSet"); - public static class Options { - @Option(help = "Instrument write barriers with counters")// - public static final HostedOptionKey CountWriteBarriers = new HostedOptionKey<>(false); - - @Option(help = "Verify write barriers")// - public static final HostedOptionKey VerifyWriteBarriers = new HostedOptionKey<>(false); - } - @Fold static BarrierSnippetCounters counters() { return ImageSingletons.lookup(BarrierSnippetCounters.class); @@ -102,7 +93,7 @@ public static void postWriteBarrierSnippet(Object object, @ConstantParameter boo Object fixedObject = FixedValueAnchorNode.getObject(object); UnsignedWord objectHeader = ObjectHeaderImpl.readHeaderFromObject(fixedObject); - if (Options.VerifyWriteBarriers.getValue() && alwaysAlignedChunk) { + if (SerialGCOptions.VerifyWriteBarriers.getValue() && alwaysAlignedChunk) { /* * To increase verification coverage, we do the verification before checking if a * barrier is needed at all. And in addition to verifying that the object is in an @@ -191,7 +182,7 @@ public static long getPostWriteBarrierUnalignedCount() { } class BarrierSnippetCounters { - private final Counter.Group counters = new Counter.Group(BarrierSnippets.Options.CountWriteBarriers, "WriteBarriers"); + private final Counter.Group counters = new Counter.Group(SerialGCOptions.CountWriteBarriers, "WriteBarriers"); final Counter postWriteBarrier = new Counter(counters, "postWriteBarrier", "post-write barriers"); final Counter postWriteBarrierAligned = new Counter(counters, "postWriteBarrierAligned", "aligned object path of post-write barriers"); final Counter postWriteBarrierUnaligned = new Counter(counters, "postWriteBarrierUnaligned", "unaligned object path of post-write barriers"); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/SerialGCFeature.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java similarity index 99% rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/SerialGCFeature.java rename to substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java index ac24f6a485bc..693d9a36bcfa 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/SerialGCFeature.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java @@ -60,7 +60,7 @@ import com.oracle.svm.core.jdk.management.ManagementSupport; @AutomaticFeature -class SerialGCFeature implements InternalFeature { +class GenScavengeGCFeature implements InternalFeature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { return SubstrateOptions.UseSerialGC.getValue(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateGCOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateGCOptions.java index 66059645ff29..c91fc3bdebde 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateGCOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateGCOptions.java @@ -24,8 +24,8 @@ */ package com.oracle.svm.core; -import com.oracle.svm.core.option.GCRuntimeOptionKey; -import com.oracle.svm.core.option.ImmutableGCRuntimeOptionKey; +import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.Immutable; + import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; @@ -33,33 +33,17 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.heap.HeapSizeVerifier; +import com.oracle.svm.core.option.NotifyGCRuntimeOptionKey; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.RuntimeOptionKey; /** - * Garbage collection-specific options that are supported by all garbage collectors. + * Garbage collection-specific options that are supported by all garbage collectors. Some of these + * options don't have any effect on the epsilon GC because it does not collect any garbage. */ public class SubstrateGCOptions { - @Option(help = "Print more information about the heap before and after each collection.", type = OptionType.Expert)// - public static final RuntimeOptionKey VerboseGC = new GCRuntimeOptionKey<>(false); - - @Option(help = "Determines if references from runtime-compiled code to Java heap objects should be treated as strong or weak.", type = OptionType.Debug)// - public static final HostedOptionKey TreatRuntimeCodeInfoReferencesAsWeak = new HostedOptionKey<>(true); - - @Option(help = "Verify the heap before and after each collection.")// - public static final HostedOptionKey VerifyHeap = new HostedOptionKey<>(false); - - @Option(help = "Ignore calls to System.gc()", type = OptionType.Expert)// - public static final RuntimeOptionKey DisableExplicitGC = new GCRuntimeOptionKey<>(false); - - @Option(help = "Print summary GC information after each collection.", type = OptionType.Expert)// - public static final RuntimeOptionKey PrintGC = new GCRuntimeOptionKey<>(false); - - @Option(help = "Exit on the first occurrence of an out-of-memory error that is thrown because the Java heap is out of memory.", type = OptionType.Expert)// - public static final RuntimeOptionKey ExitOnOutOfMemoryError = new GCRuntimeOptionKey<>(false); - @Option(help = "The minimum heap size at run-time, in bytes.", type = OptionType.User)// - public static final RuntimeOptionKey MinHeapSize = new ImmutableGCRuntimeOptionKey<>(0L) { + public static final RuntimeOptionKey MinHeapSize = new NotifyGCRuntimeOptionKey<>(0L, Immutable) { @Override protected void onValueUpdate(EconomicMap, Object> values, Long oldValue, Long newValue) { if (!SubstrateUtil.HOSTED) { @@ -70,7 +54,7 @@ protected void onValueUpdate(EconomicMap, Object> values, Long oldV }; @Option(help = "The maximum heap size at run-time, in bytes.", type = OptionType.User)// - public static final RuntimeOptionKey MaxHeapSize = new ImmutableGCRuntimeOptionKey<>(0L) { + public static final RuntimeOptionKey MaxHeapSize = new NotifyGCRuntimeOptionKey<>(0L, Immutable) { @Override protected void onValueUpdate(EconomicMap, Object> values, Long oldValue, Long newValue) { if (!SubstrateUtil.HOSTED) { @@ -81,7 +65,7 @@ protected void onValueUpdate(EconomicMap, Object> values, Long oldV }; @Option(help = "The maximum size of the young generation at run-time, in bytes", type = OptionType.User)// - public static final RuntimeOptionKey MaxNewSize = new ImmutableGCRuntimeOptionKey<>(0L) { + public static final RuntimeOptionKey MaxNewSize = new NotifyGCRuntimeOptionKey<>(0L, Immutable) { @Override protected void onValueUpdate(EconomicMap, Object> values, Long oldValue, Long newValue) { if (!SubstrateUtil.HOSTED) { @@ -90,4 +74,22 @@ protected void onValueUpdate(EconomicMap, Object> values, Long oldV super.onValueUpdate(values, oldValue, newValue); } }; + + @Option(help = "Exit on the first occurrence of an out-of-memory error that is thrown because the Java heap is out of memory.", type = OptionType.Expert)// + public static final RuntimeOptionKey ExitOnOutOfMemoryError = new NotifyGCRuntimeOptionKey<>(false); + + @Option(help = "Ignore calls to System.gc().", type = OptionType.Expert)// + public static final RuntimeOptionKey DisableExplicitGC = new NotifyGCRuntimeOptionKey<>(false); + + @Option(help = "Print summary GC information after each collection.", type = OptionType.Expert)// + public static final RuntimeOptionKey PrintGC = new NotifyGCRuntimeOptionKey<>(false); + + @Option(help = "Print more information about the heap before and after each collection.", type = OptionType.Expert)// + public static final RuntimeOptionKey VerboseGC = new NotifyGCRuntimeOptionKey<>(false); + + @Option(help = "Verify the heap before and after each collection.", type = OptionType.Debug)// + public static final HostedOptionKey VerifyHeap = new HostedOptionKey<>(false); + + @Option(help = "Determines if references from runtime-compiled code to Java heap objects should be treated as strong or weak.", type = OptionType.Debug)// + public static final HostedOptionKey TreatRuntimeCodeInfoReferencesAsWeak = new HostedOptionKey<>(true); } 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 170bc969d5d7..334ef05d24fc 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 @@ -24,6 +24,7 @@ */ package com.oracle.svm.core; +import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.Immutable; import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.RelevantForCompilationIsolates; import static org.graalvm.compiler.core.common.GraalOptions.TrackNodeSourcePosition; import static org.graalvm.compiler.core.common.SpectrePHTMitigations.None; @@ -58,7 +59,6 @@ import com.oracle.svm.core.option.APIOptionGroup; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.HostedOptionValues; -import com.oracle.svm.core.option.ImmutableRuntimeOptionKey; import com.oracle.svm.core.option.LocatableMultiOptionValue; import com.oracle.svm.core.option.OptionUtils; import com.oracle.svm.core.option.RuntimeOptionKey; @@ -450,12 +450,12 @@ public Boolean getValue(OptionValues values) { @Option(help = "The number of nanoseconds before tearing down an isolate gives a failure message. 0 implies no message.")// public static final RuntimeOptionKey TearDownFailureNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates); - public static final long getTearDownWarningNanos() { - return TearDownWarningNanos.getValue().longValue(); + public static long getTearDownWarningNanos() { + return TearDownWarningNanos.getValue(); } - public static final long getTearDownFailureNanos() { - return TearDownFailureNanos.getValue().longValue(); + public static long getTearDownFailureNanos() { + return TearDownFailureNanos.getValue(); } @Option(help = "Define the maximum number of stores for which the loop that zeroes out objects is unrolled.")// @@ -684,11 +684,11 @@ public Boolean getValue(OptionValues values) { /** Use {@link ReferenceHandler#isExecutedManually()} instead. */ @Option(help = "Determines if the reference handling is executed automatically or manually.", type = OptionType.Expert) // - public static final RuntimeOptionKey AutomaticReferenceHandling = new ImmutableRuntimeOptionKey<>(true); + public static final RuntimeOptionKey AutomaticReferenceHandling = new RuntimeOptionKey<>(true, Immutable); } @Option(help = "Overwrites the available number of processors provided by the OS. Any value <= 0 means using the processor count from the OS.")// - public static final RuntimeOptionKey ActiveProcessorCount = new ImmutableRuntimeOptionKey<>(-1, RelevantForCompilationIsolates); + public static final RuntimeOptionKey ActiveProcessorCount = new RuntimeOptionKey<>(-1, Immutable, RelevantForCompilationIsolates); @Option(help = "For internal purposes only. Disables type id result verification even when running with assertions enabled.", stability = OptionStability.EXPERIMENTAL, type = Debug)// public static final HostedOptionKey DisableTypeIdResultVerification = new HostedOptionKey<>(true); @@ -726,16 +726,16 @@ public Boolean getValue(OptionValues values) { }; @Option(help = "Create a heap dump and exit.")// - public static final RuntimeOptionKey DumpHeapAndExit = new ImmutableRuntimeOptionKey<>(false); + public static final RuntimeOptionKey DumpHeapAndExit = new RuntimeOptionKey<>(false, Immutable); @Option(help = "Enable Java Flight Recorder.")// - public static final RuntimeOptionKey FlightRecorder = new ImmutableRuntimeOptionKey<>(false); + public static final RuntimeOptionKey FlightRecorder = new RuntimeOptionKey<>(false, Immutable); @Option(help = "Start flight recording with options.")// - public static final RuntimeOptionKey StartFlightRecording = new ImmutableRuntimeOptionKey<>(""); + public static final RuntimeOptionKey StartFlightRecording = new RuntimeOptionKey<>("", Immutable); @Option(help = "file:doc-files/FlightRecorderLoggingHelp.txt")// - public static final RuntimeOptionKey FlightRecorderLogging = new ImmutableRuntimeOptionKey<>("all=warning"); + public static final RuntimeOptionKey FlightRecorderLogging = new RuntimeOptionKey<>("all=warning", Immutable); public static String reportsPath() { return Paths.get(Paths.get(Path.getValue()).toString(), ImageSingletons.lookup(ReportingSupport.class).reportsPath).toAbsolutePath().toString(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/HostedOptionKey.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/HostedOptionKey.java index 4816367709d2..d7224d5bfa59 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/HostedOptionKey.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/HostedOptionKey.java @@ -31,16 +31,24 @@ import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; +import java.util.function.Consumer; + /** * Defines a hosted {@link Option} that is used during native image generation, in contrast to a * {@link RuntimeOptionKey runtime option}. * * @see com.oracle.svm.core.option */ -public class HostedOptionKey extends OptionKey { +public class HostedOptionKey extends OptionKey implements ValidatableOptionKey { + private final Consumer> validation; public HostedOptionKey(T defaultValue) { + this(defaultValue, null); + } + + public HostedOptionKey(T defaultValue, Consumer> validation) { super(defaultValue); + this.validation = validation; } /** @@ -61,6 +69,7 @@ public T getValue() { * {@link Fold} annotation. */ @Fold + @Override public boolean hasBeenSet() { return hasBeenSet(HostedOptionValues.singleton()); } @@ -87,4 +96,11 @@ public void update(EconomicMap, Object> values, Object boxedValue) super.update(values, LocatableOption.rawValue(boxedValue)); } } + + @Override + public void validate() { + if (validation != null) { + validation.accept(this); + } + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ImmutableRuntimeOptionKey.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ImmutableRuntimeOptionKey.java deleted file mode 100644 index a0b154abe096..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ImmutableRuntimeOptionKey.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.option; - -import java.util.Objects; - -import org.graalvm.collections.EconomicMap; -import org.graalvm.compiler.options.OptionKey; -import org.graalvm.nativeimage.ImageSingletons; - -import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.jdk.RuntimeSupport; - -/** - * A runtime option that can only be modified as long as the VM is not fully initialized yet. - */ -public class ImmutableRuntimeOptionKey extends RuntimeOptionKey { - public ImmutableRuntimeOptionKey(T defaultValue, RuntimeOptionKeyFlag... flags) { - super(defaultValue, flags); - } - - @Override - @SuppressWarnings("unchecked") - public void update(EconomicMap, Object> values, Object newValue) { - if (!SubstrateUtil.HOSTED && !ImageSingletons.lookup(RuntimeSupport.class).isUninitialized() && isDifferentValue(values, newValue)) { - T value = (T) values.get(this); - throw new IllegalStateException("The runtime option '" + this.getName() + "' is immutable and can only be set during startup. Current value: " + value + ", new value: " + newValue); - } - super.update(values, newValue); - } - - @SuppressWarnings("unchecked") - private boolean isDifferentValue(EconomicMap, Object> values, Object newValue) { - if (!values.containsKey(this) && !Objects.equals(getDefaultValue(), newValue)) { - return true; - } - - T value = (T) values.get(this); - return !Objects.equals(value, newValue); - } -} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/GCRuntimeOptionKey.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/NotifyGCRuntimeOptionKey.java similarity index 81% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/GCRuntimeOptionKey.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/NotifyGCRuntimeOptionKey.java index b4aa70b929bf..5ec777e33cca 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/GCRuntimeOptionKey.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/NotifyGCRuntimeOptionKey.java @@ -27,14 +27,20 @@ import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.heap.Heap; +import java.util.function.Consumer; + /** * Notifies the {@link Heap} implementation after the value of the option has changed. */ -public class GCRuntimeOptionKey extends RuntimeOptionKey { - public GCRuntimeOptionKey(T defaultValue, RuntimeOptionKeyFlag... flags) { +public class NotifyGCRuntimeOptionKey extends RuntimeOptionKey { + public NotifyGCRuntimeOptionKey(T defaultValue, RuntimeOptionKeyFlag... flags) { super(defaultValue, flags); } + public NotifyGCRuntimeOptionKey(T defaultValue, Consumer> validation, RuntimeOptionKeyFlag... flags) { + super(defaultValue, validation, flags); + } + @Override protected void afterValueUpdate() { super.afterValueUpdate(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/RuntimeOptionKey.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/RuntimeOptionKey.java index 9c7621a98ddb..e77926a3f2cf 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/RuntimeOptionKey.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/RuntimeOptionKey.java @@ -24,20 +24,35 @@ */ package com.oracle.svm.core.option; +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.jdk.RuntimeSupport; +import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import java.util.Objects; +import java.util.function.Consumer; /** * Defines a runtime {@link Option}, in contrast to a {@link HostedOptionKey hosted option}. * * @see com.oracle.svm.core.option */ -public class RuntimeOptionKey extends OptionKey { +public class RuntimeOptionKey extends OptionKey implements ValidatableOptionKey { + private final Consumer> validation; private final int flags; public RuntimeOptionKey(T defaultValue, RuntimeOptionKeyFlag... flags) { + this(defaultValue, null, flags); + } + + public RuntimeOptionKey(T defaultValue, Consumer> validation, RuntimeOptionKeyFlag... flags) { super(defaultValue); + this.validation = validation; this.flags = computeFlags(flags); } @@ -52,12 +67,49 @@ public void update(T value) { RuntimeOptionValues.singleton().update(this, value); } + @Override + @SuppressWarnings("unchecked") + public void update(EconomicMap, Object> values, Object newValue) { + if (!SubstrateUtil.HOSTED && isImmutable() && !ImageSingletons.lookup(RuntimeSupport.class).isUninitialized() && isDifferentValue(values, newValue)) { + T value = (T) values.get(this); + throw new IllegalStateException("The runtime option '" + this.getName() + "' is immutable and can only be set during startup. Current value: " + value + ", new value: " + newValue); + } + super.update(values, newValue); + } + + @SuppressWarnings("unchecked") + private boolean isDifferentValue(EconomicMap, Object> values, Object newValue) { + if (!values.containsKey(this) && !Objects.equals(getDefaultValue(), newValue)) { + return true; + } + + T value = (T) values.get(this); + return !Objects.equals(value, newValue); + } + + @Override public boolean hasBeenSet() { return hasBeenSet(RuntimeOptionValues.singleton()); } + @Override + @Platforms(Platform.HOSTED_ONLY.class) + public void validate() { + if (validation != null) { + validation.accept(this); + } + } + public boolean shouldCopyToCompilationIsolate() { - return (flags & RuntimeOptionKeyFlag.RelevantForCompilationIsolates.ordinal()) != 0; + return hasFlag(RuntimeOptionKeyFlag.RelevantForCompilationIsolates); + } + + public boolean isImmutable() { + return hasFlag(RuntimeOptionKeyFlag.Immutable); + } + + private boolean hasFlag(RuntimeOptionKeyFlag immutable) { + return (flags & immutable.ordinal()) != 0; } @Fold @@ -75,6 +127,7 @@ private static int computeFlags(RuntimeOptionKeyFlag[] flags) { } public enum RuntimeOptionKeyFlag { - RelevantForCompilationIsolates + RelevantForCompilationIsolates, + Immutable } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ImmutableGCRuntimeOptionKey.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ValidatableOptionKey.java similarity index 63% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ImmutableGCRuntimeOptionKey.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ValidatableOptionKey.java index cae0892260ff..2f3613232417 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ImmutableGCRuntimeOptionKey.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ValidatableOptionKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, 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 @@ -24,23 +24,8 @@ */ package com.oracle.svm.core.option; -import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.heap.Heap; +public interface ValidatableOptionKey { + void validate(); -/** - * Immutable runtime option that notifies the {@link Heap} implementation once the value of the - * option was set. - */ -public class ImmutableGCRuntimeOptionKey extends ImmutableRuntimeOptionKey { - public ImmutableGCRuntimeOptionKey(T defaultValue, RuntimeOptionKeyFlag... flags) { - super(defaultValue, flags); - } - - @Override - protected void afterValueUpdate() { - super.afterValueUpdate(); - if (!SubstrateUtil.HOSTED) { - Heap.getHeap().optionValueChanged(this); - } - } + boolean hasBeenSet(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ValidateImageBuildOptionsFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ValidateImageBuildOptionsFeature.java new file mode 100644 index 000000000000..c012bed2a0cb --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/ValidateImageBuildOptionsFeature.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022, 2022, 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.option; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import org.graalvm.collections.UnmodifiableMapCursor; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.nativeimage.hosted.Feature; + +@AutomaticFeature +public class ValidateImageBuildOptionsFeature implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + UnmodifiableMapCursor, Object> cursor = RuntimeOptionValues.singleton().getMap().getEntries(); + while (cursor.advance()) { + validate(cursor.getKey()); + } + + cursor = HostedOptionValues.singleton().getMap().getEntries(); + while (cursor.advance()) { + validate(cursor.getKey()); + } + } + + private static void validate(OptionKey option) { + if (option instanceof ValidatableOptionKey) { + ValidatableOptionKey o = (ValidatableOptionKey) option; + if (o.hasBeenSet()) { + o.validate(); + } + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/InterruptImageBuilding.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/InterruptImageBuilding.java index c5ba4905ced4..2f74c491178e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/InterruptImageBuilding.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/InterruptImageBuilding.java @@ -24,11 +24,15 @@ */ package com.oracle.svm.core.util; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + import java.util.Optional; /** * Used to report valid interruption of compilation. */ +@Platforms(Platform.HOSTED_ONLY.class) public class InterruptImageBuilding extends RuntimeException { static final long serialVersionUID = 754312906378380L; private final boolean hasMessage;