diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AddressRangeCommittedMemoryProvider.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AddressRangeCommittedMemoryProvider.java
index f9c06ee7c8d2..e6050901f726 100644
--- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AddressRangeCommittedMemoryProvider.java
+++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AddressRangeCommittedMemoryProvider.java
@@ -61,6 +61,7 @@
import com.oracle.svm.core.locks.VMMutex;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.memory.NullableNativeMemory;
+import com.oracle.svm.core.metaspace.Metaspace;
import com.oracle.svm.core.nmt.NativeMemoryTracking;
import com.oracle.svm.core.nmt.NmtCategory;
import com.oracle.svm.core.os.ChunkBasedCommittedMemoryProvider;
@@ -77,7 +78,8 @@
/**
* Reserves a fixed-size address range and provides memory from it by committing and uncommitting
- * virtual memory within that range.
+ * virtual memory within that range. The address space is shared by the null regions, the
+ * {@link Metaspace}, the image heap, and the collected Java heap.
*
* The main objective of this code is to keep external fragmentation low so that an
* {@linkplain Isolate} is unlikely to run out of memory because its address space is exhausted. To
@@ -122,20 +124,26 @@ public class AddressRangeCommittedMemoryProvider extends ChunkBasedCommittedMemo
*/
private final VMMutex lock = new VMMutex("freeList");
- /** Contains free blocks that are large enough to fit allocations. */
+ protected UnsignedWord reservedAddressSpaceSize;
+ private Pointer metaspaceBegin;
+ private Pointer metaspaceTop;
+ private Pointer metaspaceEnd;
+ protected Pointer collectedHeapBegin;
+ protected UnsignedWord collectedHeapSize;
+
+ /**
+ * Contains free blocks for the collected Java heap that are large enough to fit allocations.
+ */
protected FreeListNode allocListHead;
protected long allocListCount;
- /** Contains all free blocks, including small blocks that are needed for coalescing. */
+ /**
+ * Contains all free blocks for the collected Java heap, including small blocks that are needed
+ * for coalescing.
+ */
protected FreeListNode unusedListHead;
protected long unusedListCount;
- protected UnsignedWord reservedAddressSpaceSize;
- protected UnsignedWord reservedMetaspaceSize;
-
- protected Pointer collectedHeapBegin;
- protected UnsignedWord collectedHeapSize;
-
@Platforms(Platform.HOSTED_ONLY.class)
public AddressRangeCommittedMemoryProvider() {
assert SubstrateOptions.SpawnIsolates.getValue();
@@ -143,38 +151,38 @@ public AddressRangeCommittedMemoryProvider() {
@Override
@Uninterruptible(reason = "Still being initialized.")
- public int initialize(WordPointer heapBasePointer, IsolateArguments arguments) {
- UnsignedWord reserved = Word.unsigned(IsolateArgumentAccess.readLong(arguments, IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.ReservedAddressSpaceSize)));
- if (reserved.equal(0)) {
+ public int initialize(WordPointer heapBaseOut, IsolateArguments arguments) {
+ UnsignedWord reservedSize = Word.unsigned(IsolateArgumentAccess.readLong(arguments, IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.ReservedAddressSpaceSize)));
+ if (reservedSize.equal(0)) {
/*
* Reserve a 32 GB address space, except if a larger heap size was specified, or if the
* maximum address space size is less than that.
*/
UnsignedWord maxHeapSize = Word.unsigned(IsolateArgumentAccess.readLong(arguments, IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.MaxHeapSize)));
- reserved = UnsignedUtils.max(maxHeapSize, Word.unsigned(MIN_RESERVED_ADDRESS_SPACE_SIZE));
+ reservedSize = UnsignedUtils.max(maxHeapSize, Word.unsigned(MIN_RESERVED_ADDRESS_SPACE_SIZE));
}
- reserved = UnsignedUtils.min(reserved, ReferenceAccess.singleton().getMaxAddressSpaceSize());
+ reservedSize = UnsignedUtils.min(reservedSize, ReferenceAccess.singleton().getMaxAddressSpaceSize());
- UnsignedWord alignment = unsigned(Heap.getHeap().getPreferredAddressSpaceAlignment());
- WordPointer beginOut = StackValue.get(WordPointer.class);
- int errorCode = reserveHeapMemory(reserved, alignment, arguments, beginOut);
+ UnsignedWord alignment = unsigned(Heap.getHeap().getHeapBaseAlignment());
+ WordPointer reservedBeginPtr = StackValue.get(WordPointer.class);
+ int errorCode = reserveHeapMemory(reservedSize, alignment, arguments, reservedBeginPtr);
if (errorCode != CEntryPointErrors.NO_ERROR) {
return errorCode;
}
- Pointer begin = beginOut.read();
+ Pointer reservedBegin = reservedBeginPtr.read();
WordPointer imageHeapEndOut = StackValue.get(WordPointer.class);
- errorCode = ImageHeapProvider.get().initialize(begin, reserved, heapBasePointer, imageHeapEndOut);
+ errorCode = ImageHeapProvider.get().initialize(reservedBegin, reservedSize, heapBaseOut, imageHeapEndOut);
if (errorCode != CEntryPointErrors.NO_ERROR) {
- freeOnInitializeError(begin, reserved);
+ freeOnInitializeError(reservedBegin, reservedSize);
return errorCode;
}
- CEntryPointSnippets.initBaseRegisters(heapBasePointer.read());
+ CEntryPointSnippets.initBaseRegisters(heapBaseOut.read());
WordPointer runtimeHeapBeginOut = StackValue.get(WordPointer.class);
- errorCode = getCollectedHeapBegin(arguments, begin, reserved, imageHeapEndOut.read(), runtimeHeapBeginOut);
+ errorCode = initializeCollectedHeapBegin(arguments, reservedBegin, reservedSize, imageHeapEndOut.read(), runtimeHeapBeginOut);
if (errorCode != CEntryPointErrors.NO_ERROR) {
- freeOnInitializeError(begin, reserved);
+ freeOnInitializeError(reservedBegin, reservedSize);
return errorCode;
}
@@ -183,40 +191,58 @@ public int initialize(WordPointer heapBasePointer, IsolateArguments arguments) {
* because the image heap was not initialized when we were called, so we invoke a static
* method that loads a new reference to our instance.
*/
- errorCode = initialize(begin, reserved, runtimeHeapBeginOut.read());
+ errorCode = initialize(reservedBegin, reservedSize, runtimeHeapBeginOut.read());
if (errorCode != CEntryPointErrors.NO_ERROR) {
- freeOnInitializeError(begin, reserved);
+ freeOnInitializeError(reservedBegin, reservedSize);
}
return errorCode;
}
@Uninterruptible(reason = "Still being initialized.")
- protected int getCollectedHeapBegin(@SuppressWarnings("unused") IsolateArguments arguments, @SuppressWarnings("unused") Pointer begin, @SuppressWarnings("unused") UnsignedWord reserved,
- Pointer imageHeapEnd, WordPointer collectedHeapBeginOut) {
- Pointer result = roundUp(imageHeapEnd, getGranularity());
- collectedHeapBeginOut.write(result);
+ protected int initializeCollectedHeapBegin(@SuppressWarnings("unused") IsolateArguments arguments, @SuppressWarnings("unused") Pointer reservedBegin,
+ @SuppressWarnings("unused") UnsignedWord reservedSize, Pointer imageHeapEnd, WordPointer collectedHeapBeginOut) {
+ assert PointerUtils.isAMultiple(imageHeapEnd, Word.unsigned(SubstrateOptions.getPageSize()));
+ collectedHeapBeginOut.write(imageHeapEnd);
return CEntryPointErrors.NO_ERROR;
}
@NeverInline("Ensure a newly looked up value is used as 'this', now that the image heap is initialized")
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE)
- private static int initialize(Pointer spaceBegin, UnsignedWord spaceSize, Pointer collectedHeapBegin) {
- if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) {
- UnsignedWord imageHeapAddressSpace = ImageHeapProvider.get().getImageHeapAddressSpaceSize();
- UnsignedWord javaHeapAddressSpace = spaceSize.subtract(imageHeapAddressSpace);
- NativeMemoryTracking.singleton().trackReserve(javaHeapAddressSpace, NmtCategory.JavaHeap);
- }
-
+ private static int initialize(Pointer reservedBegin, UnsignedWord reservedSize, Pointer collectedHeapBegin) {
AddressRangeCommittedMemoryProvider provider = (AddressRangeCommittedMemoryProvider) ChunkBasedCommittedMemoryProvider.get();
- return provider.initializeFields(spaceBegin, spaceSize, collectedHeapBegin);
+ return provider.initializeFields(reservedBegin, reservedSize, collectedHeapBegin);
}
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
@SuppressWarnings("hiding")
+ protected int initializeFields(Pointer reservedBegin, UnsignedWord reservedSize, Pointer collectedHeapBegin) {
+ this.reservedAddressSpaceSize = reservedSize;
+
+ initializeMetaspaceFields();
+ return initializeCollectedHeapFields(reservedBegin, reservedSize, collectedHeapBegin);
+ }
+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
- protected int initializeFields(Pointer spaceBegin, UnsignedWord reservedSpaceSize, Pointer collectedHeapBegin) {
- this.reservedAddressSpaceSize = reservedSpaceSize;
+ protected void initializeMetaspaceFields() {
+ int metaspaceSize = SerialAndEpsilonGCOptions.getReservedMetaspaceSize();
+ this.metaspaceBegin = KnownIntrinsics.heapBase().add(HeapImpl.getMetaspaceOffsetInAddressSpace());
+ this.metaspaceTop = metaspaceBegin;
+ this.metaspaceEnd = metaspaceTop.add(metaspaceSize);
+
+ if (VMInspectionOptions.hasNativeMemoryTrackingSupport() && metaspaceSize > 0) {
+ NativeMemoryTracking.singleton().trackReserve(metaspaceSize, NmtCategory.Metaspace);
+ }
+ }
+
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ @SuppressWarnings("hiding")
+ private int initializeCollectedHeapFields(Pointer reservedBegin, UnsignedWord reservedSize, Pointer collectedHeapBegin) {
this.collectedHeapBegin = collectedHeapBegin;
- this.collectedHeapSize = spaceBegin.add(reservedSpaceSize).subtract(collectedHeapBegin);
+ this.collectedHeapSize = reservedSize.subtract(collectedHeapBegin.subtract(reservedBegin));
+
+ if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) {
+ NativeMemoryTracking.singleton().trackReserve(collectedHeapSize, NmtCategory.JavaHeap);
+ }
FreeListNode node = allocNodeOrNull(collectedHeapBegin, collectedHeapSize);
if (node.isNull()) {
@@ -227,10 +253,20 @@ protected int initializeFields(Pointer spaceBegin, UnsignedWord reservedSpaceSiz
this.unusedListCount = 1;
this.allocListHead = node;
this.allocListCount = 1;
-
return CEntryPointErrors.NO_ERROR;
}
+ @Override
+ public UnsignedWord getCollectedHeapAddressSpaceSize() {
+ return collectedHeapSize;
+ }
+
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ public boolean isInMetaspace(Pointer ptr) {
+ /* Checking against begin and end does not need any locking. */
+ return ptr.aboveOrEqual(metaspaceBegin) && ptr.belowThan(metaspaceEnd);
+ }
+
@Uninterruptible(reason = "Still being initialized.")
protected int reserveHeapMemory(UnsignedWord reserved, UnsignedWord alignment, IsolateArguments arguments, WordPointer beginOut) {
Pointer begin = reserveHeapMemory0(reserved, alignment, arguments);
@@ -329,29 +365,48 @@ protected int unmapAddressSpace(PointerBase heapBase) {
}
@Override
- @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ @Uninterruptible(reason = "Locking without transition requires that the whole critical section is uninterruptible.")
public Pointer allocateMetaspaceChunk(UnsignedWord nbytes, UnsignedWord alignment) {
- WordPointer allocOut = UnsafeStackValue.get(WordPointer.class);
- int error = allocateInHeapAddressSpace(nbytes, alignment, allocOut);
- if (error == NO_ERROR) {
- if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) {
- NativeMemoryTracking.singleton().trackCommit(nbytes, NmtCategory.Metaspace);
- }
- return allocOut.read();
+ lock.lockNoTransition();
+ try {
+ return allocateMetaspaceChunk0(nbytes, alignment);
+ } finally {
+ lock.unlock();
}
- throw reportMetaspaceChunkAllocationFailed(error);
}
- @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
- protected OutOfMemoryError reportMetaspaceChunkAllocationFailed(int error) {
- /* Explicitly don't use OutOfMemoryUtil as the metaspace is not part of the Java heap. */
- if (error == OUT_OF_ADDRESS_SPACE) {
+ /**
+ * This method intentionally does not use {@link OutOfMemoryUtil} when reporting
+ * {@link OutOfMemoryError}s as the metaspace is not part of the Java heap.
+ */
+ @Uninterruptible(reason = "Locking without transition requires that the whole critical section is uninterruptible.")
+ private Pointer allocateMetaspaceChunk0(UnsignedWord nbytes, UnsignedWord alignment) {
+ assert lock.isOwner();
+
+ Pointer result = metaspaceTop;
+ Pointer newTop = metaspaceTop.add(nbytes);
+ assert result.isNonNull();
+ assert PointerUtils.isAMultiple(result, alignment);
+ assert UnsignedUtils.isAMultiple(newTop, alignment);
+
+ /* Check if the allocation fits into the reserved address space. */
+ if (newTop.aboveThan(metaspaceEnd)) {
throw OUT_OF_METASPACE;
- } else if (error == COMMIT_FAILED) {
+ }
+
+ /* Try to commit the memory. */
+ int access = VirtualMemoryProvider.Access.READ | VirtualMemoryProvider.Access.WRITE;
+ Pointer actualBegin = VirtualMemoryProvider.get().commit(result, nbytes, access);
+ if (actualBegin.isNull()) {
throw METASPACE_CHUNK_COMMIT_FAILED;
- } else {
- throw VMError.shouldNotReachHereAtRuntime();
}
+
+ /* Update top and NMT statistics. */
+ metaspaceTop = newTop;
+ if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) {
+ NativeMemoryTracking.singleton().trackCommit(nbytes, NmtCategory.Metaspace);
+ }
+ return actualBegin;
}
@Override
@@ -821,11 +876,6 @@ public UnsignedWord getReservedAddressSpaceSize() {
return reservedAddressSpaceSize;
}
- @Override
- public UnsignedWord getReservedMetaspaceSize() {
- return reservedMetaspaceSize;
- }
-
/** Keeps track of unused memory. */
@RawStructure
protected interface FreeListNode extends PointerBase {
diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ChunkedImageHeapLayouter.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ChunkedImageHeapLayouter.java
index 793378810103..ecbfa917f63a 100644
--- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ChunkedImageHeapLayouter.java
+++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ChunkedImageHeapLayouter.java
@@ -36,6 +36,7 @@
import com.oracle.svm.core.genscavenge.ChunkedImageHeapAllocator.Chunk;
import com.oracle.svm.core.genscavenge.ChunkedImageHeapAllocator.UnalignedChunk;
import com.oracle.svm.core.genscavenge.remset.RememberedSet;
+import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.image.ImageHeap;
import com.oracle.svm.core.image.ImageHeapLayoutInfo;
@@ -88,6 +89,8 @@ public class ChunkedImageHeapLayouter implements ImageHeapLayouter {
/** @param startOffset Offset relative to the heap base. */
@SuppressWarnings("this-escape")
public ChunkedImageHeapLayouter(ImageHeapInfo heapInfo, long startOffset) {
+ assert startOffset % Heap.getHeap().getImageHeapAlignment() == 0 : "the start of each image heap must be aligned";
+
this.partitions = new ChunkedImageHeapPartition[PARTITION_COUNT];
this.partitions[READ_ONLY_REGULAR] = new ChunkedImageHeapPartition("readOnly", false, false);
this.partitions[READ_ONLY_RELOCATABLE] = new ChunkedImageHeapPartition("readOnlyRelocatable", false, false);
@@ -98,6 +101,7 @@ public ChunkedImageHeapLayouter(ImageHeapInfo heapInfo, long startOffset) {
this.heapInfo = heapInfo;
this.startOffset = startOffset;
+
UnsignedWord alignedHeaderSize = RememberedSet.get().getHeaderSizeOfAlignedChunk();
UnsignedWord hugeThreshold = HeapParameters.getAlignedHeapChunkSize().subtract(alignedHeaderSize);
this.hugeObjectThreshold = hugeThreshold.rawValue();
@@ -172,7 +176,6 @@ public ImageHeapLayoutInfo layout(ImageHeap imageHeap, int pageSize, ImageHeapLa
assert partition.getStartOffset() % objectAlignment == 0 : partition;
assert (partition.getStartOffset() + partition.getSize()) % objectAlignment == 0 : partition;
}
- assert layoutInfo.getImageHeapSize() % pageSize == 0 : "Image heap size is not a multiple of page size";
return layoutInfo;
}
@@ -223,8 +226,7 @@ private ImageHeapLayoutInfo populateInfoObjects(int dynamicHubCount, int pageSiz
long writableSize = writableEnd - offsetOfFirstWritableAlignedChunk;
/* Aligning the end to the page size can be required for mapping into memory. */
long imageHeapEnd = NumUtil.roundUp(getReadOnlyHuge().getStartOffset() + getReadOnlyHuge().getSize(), pageSize);
- long imageHeapSize = imageHeapEnd - startOffset;
- return new ImageHeapLayoutInfo(startOffset, imageHeapSize, offsetOfFirstWritableAlignedChunk, writableSize, getReadOnlyRelocatable().getStartOffset(), getReadOnlyRelocatable().getSize(),
+ return new ImageHeapLayoutInfo(startOffset, imageHeapEnd, offsetOfFirstWritableAlignedChunk, writableSize, getReadOnlyRelocatable().getStartOffset(), getReadOnlyRelocatable().getSize(),
getWritablePatched().getStartOffset(), getWritablePatched().getSize());
}
diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompactingOldGeneration.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompactingOldGeneration.java
index 8522d4e5c069..a7b2df07733d 100644
--- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompactingOldGeneration.java
+++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompactingOldGeneration.java
@@ -53,6 +53,7 @@
import com.oracle.svm.core.heap.ObjectHeader;
import com.oracle.svm.core.heap.ObjectVisitor;
import com.oracle.svm.core.log.Log;
+import com.oracle.svm.core.metaspace.Metaspace;
import com.oracle.svm.core.thread.VMThreads;
import com.oracle.svm.core.threadlocal.VMThreadLocalSupport;
import com.oracle.svm.core.util.Timer;
@@ -361,7 +362,7 @@ private void fixupImageHeapRoots(ImageHeapInfo info) {
@Uninterruptible(reason = "Avoid unnecessary safepoint checks in GC for performance.")
private void fixupMetaspace() {
- if (!MetaspaceImpl.isSupported()) {
+ if (!Metaspace.isSupported()) {
return;
}
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 a638857be6f5..c44a2cbe986d 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
@@ -92,6 +92,7 @@
import com.oracle.svm.core.jfr.JfrTicks;
import com.oracle.svm.core.jfr.events.AllocationRequiringGCEvent;
import com.oracle.svm.core.log.Log;
+import com.oracle.svm.core.metaspace.Metaspace;
import com.oracle.svm.core.os.ChunkBasedCommittedMemoryProvider;
import com.oracle.svm.core.snippets.ImplicitExceptions;
import com.oracle.svm.core.snippets.KnownIntrinsics;
@@ -968,7 +969,7 @@ private void blackenDirtyCardRoots() {
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
private void blackenMetaspace() {
- if (!MetaspaceImpl.isSupported()) {
+ if (!Metaspace.isSupported()) {
return;
}
diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeGCFeature.java
similarity index 88%
rename from substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java
rename to substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeGCFeature.java
index 79020418b0fb..f8ec2daeda8e 100644
--- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java
+++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GenScavengeGCFeature.java
@@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-package com.oracle.svm.core.genscavenge.graal;
+package com.oracle.svm.core.genscavenge;
import java.util.Arrays;
import java.util.List;
@@ -37,15 +37,10 @@
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
-import com.oracle.svm.core.genscavenge.AddressRangeCommittedMemoryProvider;
-import com.oracle.svm.core.genscavenge.ChunkedImageHeapLayouter;
-import com.oracle.svm.core.genscavenge.GenScavengeMemoryPoolMXBeans;
-import com.oracle.svm.core.genscavenge.HeapImpl;
-import com.oracle.svm.core.genscavenge.HeapVerifier;
-import com.oracle.svm.core.genscavenge.ImageHeapInfo;
-import com.oracle.svm.core.genscavenge.PinnedObjectSupportImpl;
-import com.oracle.svm.core.genscavenge.SerialGCOptions;
-import com.oracle.svm.core.genscavenge.TlabOptionCache;
+import com.oracle.svm.core.genscavenge.graal.BarrierSnippets;
+import com.oracle.svm.core.genscavenge.graal.GenScavengeAllocationSnippets;
+import com.oracle.svm.core.genscavenge.graal.GenScavengeAllocationSupport;
+import com.oracle.svm.core.genscavenge.graal.GenScavengeRelatedMXBeans;
import com.oracle.svm.core.genscavenge.jvmstat.EpsilonGCPerfData;
import com.oracle.svm.core.genscavenge.jvmstat.SerialGCPerfData;
import com.oracle.svm.core.genscavenge.metaspace.MetaspaceImpl;
@@ -62,6 +57,7 @@
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.hub.RuntimeClassLoading;
import com.oracle.svm.core.image.ImageHeapLayouter;
+import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.jdk.RuntimeSupportFeature;
import com.oracle.svm.core.jdk.SystemPropertiesSupport;
@@ -73,6 +69,7 @@
import com.oracle.svm.core.os.CommittedMemoryProvider;
import com.oracle.svm.core.os.OSCommittedMemoryProvider;
+import jdk.graal.compiler.core.common.NumUtil;
import jdk.graal.compiler.graph.Node;
import jdk.graal.compiler.options.OptionValues;
import jdk.graal.compiler.phases.util.Providers;
@@ -100,9 +97,7 @@ public void afterRegistration(AfterRegistrationAccess access) {
ImageSingletons.add(GCRelatedMXBeans.class, new GenScavengeRelatedMXBeans(memoryPoolMXBeans));
if (RuntimeClassLoading.isSupported()) {
- MetaspaceImpl metaspace = new MetaspaceImpl();
- ImageSingletons.add(Metaspace.class, metaspace);
- ImageSingletons.add(MetaspaceImpl.class, metaspace);
+ ImageSingletons.add(Metaspace.class, new MetaspaceImpl());
}
}
@@ -123,6 +118,8 @@ public void duringSetup(DuringSetupAccess access) {
if (SubstrateGCOptions.VerifyHeap.getValue()) {
ImageSingletons.add(HeapVerifier.class, new HeapVerifier());
}
+
+ HeapParameters.initialize();
}
@Override
@@ -162,10 +159,19 @@ private static ImageHeapInfo getCurrentLayerImageHeapInfo() {
@Override
public void afterAnalysis(AfterAnalysisAccess access) {
- ImageHeapLayouter heapLayouter = new ChunkedImageHeapLayouter(getCurrentLayerImageHeapInfo(), Heap.getHeap().getImageHeapOffsetInAddressSpace());
+ ImageHeapLayouter heapLayouter = new ChunkedImageHeapLayouter(getCurrentLayerImageHeapInfo(), getCurrentLayerImageHeapStartOffset());
ImageSingletons.add(ImageHeapLayouter.class, heapLayouter);
}
+ private static long getCurrentLayerImageHeapStartOffset() {
+ if (ImageLayerBuildingSupport.buildingExtensionLayer()) {
+ /* To avoid unnecessary padding, each layer's image heap starts at an aligned offset. */
+ return NumUtil.roundUp(DynamicImageLayerInfo.singleton().getPreviousImageHeapEndOffset(), Heap.getHeap().getImageHeapAlignment());
+ } else {
+ return Heap.getHeap().getImageHeapOffsetInAddressSpace();
+ }
+ }
+
@Override
public void beforeCompilation(BeforeCompilationAccess access) {
access.registerAsImmutable(getCurrentLayerImageHeapInfo());
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 4507a339cf19..2806ad66f21a 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
@@ -64,7 +64,6 @@
import com.oracle.svm.core.heap.RestrictHeapAccess;
import com.oracle.svm.core.heap.RuntimeCodeInfoGCSupport;
import com.oracle.svm.core.hub.DynamicHub;
-import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.jfr.JfrTicks;
import com.oracle.svm.core.jfr.events.SystemGCEvent;
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
@@ -75,7 +74,6 @@
import com.oracle.svm.core.nodes.CFunctionEpilogueNode;
import com.oracle.svm.core.nodes.CFunctionPrologueNode;
import com.oracle.svm.core.option.RuntimeOptionKey;
-import com.oracle.svm.core.os.ImageHeapProvider;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.thread.PlatformThreads;
import com.oracle.svm.core.thread.ThreadStatus;
@@ -88,7 +86,6 @@
import jdk.graal.compiler.api.directives.GraalDirectives;
import jdk.graal.compiler.api.replacements.Fold;
-import jdk.graal.compiler.core.common.NumUtil;
import jdk.graal.compiler.core.common.SuppressFBWarnings;
import jdk.graal.compiler.nodes.extended.MembarNode;
import jdk.graal.compiler.word.Word;
@@ -123,7 +120,6 @@ public HeapImpl() {
this.runtimeCodeInfoGcSupport = new RuntimeCodeInfoGCSupportImpl();
this.oldGeneration = SerialGCOptions.useCompactingOldGen() ? new CompactingOldGeneration("OldGeneration")
: new CopyingOldGeneration("OldGeneration");
- HeapParameters.initialize();
DiagnosticThunkRegistry.singleton().add(new DumpHeapSettingsAndStatistics());
DiagnosticThunkRegistry.singleton().add(new DumpHeapUsage());
DiagnosticThunkRegistry.singleton().add(new DumpGCPolicy());
@@ -211,7 +207,7 @@ public boolean tearDown() {
oldGeneration.tearDown();
getChunkProvider().tearDown();
- if (MetaspaceImpl.isSupported()) {
+ if (Metaspace.isSupported()) {
MetaspaceImpl.singleton().tearDown();
}
return true;
@@ -274,7 +270,7 @@ public OldGeneration getOldGeneration() {
}
void logUsage(Log log) {
- if (MetaspaceImpl.isSupported()) {
+ if (Metaspace.isSupported()) {
MetaspaceImpl.singleton().logUsage(log);
}
youngGeneration.logUsage(log);
@@ -282,7 +278,7 @@ void logUsage(Log log) {
}
void logChunks(Log log, boolean allowUnsafe) {
- if (MetaspaceImpl.isSupported()) {
+ if (Metaspace.isSupported()) {
MetaspaceImpl.singleton().logChunks(log);
}
getYoungGeneration().logChunks(log, allowUnsafe);
@@ -328,7 +324,7 @@ public int getClassCount() {
}
@Override
- protected List> getAllClasses() {
+ protected List> getClassesInImageHeap() {
/* Two threads might race to set classList, but they compute the same result. */
if (classList == null) {
ArrayList> list = findAllDynamicHubs();
@@ -443,32 +439,34 @@ public static boolean usesImageHeapCardMarking() {
@Fold
@Override
- public int getPreferredAddressSpaceAlignment() {
+ public int getHeapBaseAlignment() {
+ return getImageHeapAlignment();
+ }
+
+ @Fold
+ @Override
+ public int getImageHeapAlignment() {
return UnsignedUtils.safeToInt(HeapParameters.getAlignedHeapChunkAlignment());
}
+ @Fold
+ static int getMetaspaceOffsetInAddressSpace() {
+ assert SubstrateOptions.SpawnIsolates.getValue();
+ int result = SerialAndEpsilonGCOptions.getNullRegionSize();
+ assert result % HeapParameters.getAlignedHeapChunkAlignment().rawValue() == 0 : "start of metaspace must be aligned";
+ return result;
+ }
+
@Fold
@Override
public int getImageHeapOffsetInAddressSpace() {
- int imageHeapOffset = 0;
- if (SubstrateOptions.SpawnIsolates.getValue() && SubstrateOptions.UseNullRegion.getValue()) {
- /*
- * The image heap will be mapped in a way that there is a memory protected gap between
- * the heap base and the start of the image heap. The gap won't need any memory in the
- * native image file.
- */
- imageHeapOffset = NumUtil.safeToInt(SerialAndEpsilonGCOptions.AlignedHeapChunkSize.getValue());
+ if (!SubstrateOptions.SpawnIsolates.getValue()) {
+ return 0;
}
- if (ImageLayerBuildingSupport.buildingExtensionLayer()) {
- /*
- * GR-53964: The page size used to round up the start offset should be the same as the
- * one used in run time.
- */
- int runtimePageSize = 4096;
- imageHeapOffset = NumUtil.roundUp(imageHeapOffset + NumUtil.safeToInt(startOffset), runtimePageSize);
- }
- return imageHeapOffset;
+ int result = SerialAndEpsilonGCOptions.getNullRegionSize() + SerialAndEpsilonGCOptions.getReservedMetaspaceSize();
+ assert result % getImageHeapAlignment() == 0 : "start of image heap must be aligned";
+ return result;
}
@Fold
@@ -476,17 +474,6 @@ public static boolean isImageHeapAligned() {
return SubstrateOptions.SpawnIsolates.getValue();
}
- @Override
- public UnsignedWord getImageHeapReservedBytes() {
- return ImageHeapProvider.get().getImageHeapAddressSpaceSize();
- }
-
- @Override
- public UnsignedWord getImageHeapCommittedBytes() {
- int imageHeapOffset = HeapImpl.getHeapImpl().getImageHeapOffsetInAddressSpace();
- return ImageHeapProvider.get().getImageHeapAddressSpaceSize().subtract(imageHeapOffset);
- }
-
@Override
public void walkImageHeapObjects(ObjectVisitor visitor) {
VMOperation.guaranteeInProgressAtSafepoint("Must only be called at a safepoint");
@@ -804,7 +791,7 @@ private boolean printLocationInfo(Log log, Pointer ptr, boolean allowJavaHeapAcc
if (allowUnsafeOperations || VMOperation.isInProgressAtSafepoint()) {
/* Accessing the metaspace is unsafe due to possible concurrent modifications. */
- if (MetaspaceImpl.isSupported() && MetaspaceImpl.singleton().printLocationInfo(log, ptr)) {
+ if (Metaspace.isSupported() && MetaspaceImpl.singleton().printLocationInfo(log, ptr)) {
return true;
}
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 7f20d385982f..23c68c8011b2 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
@@ -29,9 +29,11 @@
import org.graalvm.word.UnsignedWord;
import com.oracle.svm.core.SubstrateGCOptions;
+import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.genscavenge.remset.RememberedSet;
+import com.oracle.svm.core.hub.RuntimeClassLoading;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
@@ -40,6 +42,16 @@
/** Constants and variables for the size and layout of the heap and behavior of the collector. */
public final class HeapParameters {
+ /** Freshly committed but still uninitialized Java heap memory. */
+ private static final int UNINITIALIZED_JAVA_HEAP = 0xbaadbabe;
+ private static final int UNUSED_BUT_COMMITTED_JAVA_HEAP = 0xdeadbeef;
+
+ private static final UnsignedWord producedHeapChunkZapInt = Word.unsigned(UNINITIALIZED_JAVA_HEAP);
+ private static final UnsignedWord producedHeapChunkZapWord = producedHeapChunkZapInt.shiftLeft(32).or(producedHeapChunkZapInt);
+
+ private static final UnsignedWord consumedHeapChunkZapInt = Word.unsigned(UNUSED_BUT_COMMITTED_JAVA_HEAP);
+ private static final UnsignedWord consumedHeapChunkZapWord = consumedHeapChunkZapInt.shiftLeft(32).or(consumedHeapChunkZapInt);
+
@Platforms(Platform.HOSTED_ONLY.class)
static void initialize() {
long alignedChunkSize = getAlignedHeapChunkSize().rawValue();
@@ -56,6 +68,8 @@ static void initialize() {
throw UserError.abort("LargeArrayThreshold (set to %d) should be between 1 and the usable size of an aligned chunk + 1 (currently %d).",
largeArrayThreshold, maxLargeArrayThreshold);
}
+
+ validateMaxMetaSpaceSize(alignedChunkSize);
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
@@ -159,28 +173,30 @@ public static boolean getZapConsumedHeapChunks() {
return SerialAndEpsilonGCOptions.ZapChunks.getValue() || SerialAndEpsilonGCOptions.ZapConsumedHeapChunks.getValue();
}
- static {
- /* Calling this method ensures that the static initializer has been executed. */
- }
-
- /** Freshly committed but still uninitialized Java heap memory. */
- private static final int UNINITIALIZED_JAVA_HEAP = 0xbaadbabe;
- /** Unused but still committed Java heap memory. */
- private static final int UNUSED_JAVA_HEAP = 0xdeadbeef;
-
- private static final UnsignedWord producedHeapChunkZapInt = Word.unsigned(UNINITIALIZED_JAVA_HEAP);
- private static final UnsignedWord producedHeapChunkZapWord = producedHeapChunkZapInt.shiftLeft(32).or(producedHeapChunkZapInt);
-
- private static final UnsignedWord consumedHeapChunkZapInt = Word.unsigned(UNUSED_JAVA_HEAP);
- private static final UnsignedWord consumedHeapChunkZapWord = consumedHeapChunkZapInt.shiftLeft(32).or(consumedHeapChunkZapInt);
-
- public static final class TestingBackDoor {
- private TestingBackDoor() {
+ private static void validateMaxMetaSpaceSize(long alignedChunkSize) {
+ long maxMetaspaceSize = SerialAndEpsilonGCOptions.ConcealedOptions.MaxMetaspaceSize.getValue();
+ if (maxMetaspaceSize == 0) {
+ return;
}
- /** The size, in bytes, of what qualifies as a "large" array. */
- public static long getUnalignedObjectSize() {
- return HeapParameters.getLargeArrayThreshold().rawValue();
+ if (!RuntimeClassLoading.isSupported()) {
+ throw UserError.abort("'%s' can only be set if '%s' is enabled.",
+ SerialAndEpsilonGCOptions.ConcealedOptions.MaxMetaspaceSize.getName(),
+ RuntimeClassLoading.Options.RuntimeClassLoading.getName());
+ } else if (!SubstrateOptions.SpawnIsolates.getValue()) {
+ throw UserError.abort("'%s' can only be set if '%s' is enabled.",
+ SerialAndEpsilonGCOptions.ConcealedOptions.MaxMetaspaceSize.getName(),
+ SubstrateOptions.SpawnIsolates.getName());
+ } else if (maxMetaspaceSize < 0) {
+ throw UserError.abort("The value of '%s' must be greater than or equal to 0.",
+ SerialAndEpsilonGCOptions.ConcealedOptions.MaxMetaspaceSize.getName());
+ } else if (maxMetaspaceSize % alignedChunkSize != 0) {
+ throw UserError.abort("The value of '%s' (currently '%d') must be a multiple of '%s' (currently '%d').",
+ SerialAndEpsilonGCOptions.ConcealedOptions.MaxMetaspaceSize.getName(), maxMetaspaceSize,
+ SerialAndEpsilonGCOptions.AlignedHeapChunkSize.getName(), alignedChunkSize);
+ } else if (HeapImpl.getHeap().getImageHeapOffsetInAddressSpace() < 0) {
+ throw UserError.abort("The value of '%s' is too large.",
+ SerialAndEpsilonGCOptions.ConcealedOptions.MaxMetaspaceSize.getName());
}
}
}
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 d716e0b91f46..b469d1d20606 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
@@ -46,6 +46,7 @@
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.hub.InteriorObjRefWalker;
import com.oracle.svm.core.log.Log;
+import com.oracle.svm.core.metaspace.Metaspace;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import jdk.graal.compiler.api.replacements.Fold;
@@ -84,7 +85,7 @@ protected boolean verifyImageHeap() {
}
private static boolean verifyMetaspace() {
- if (!MetaspaceImpl.isSupported()) {
+ if (!Metaspace.isSupported()) {
return true;
}
return MetaspaceImpl.singleton().verify();
@@ -144,7 +145,7 @@ private static boolean verifyRememberedSets() {
success &= rememberedSet.verify(info.getFirstWritableUnalignedChunk(), info.getLastWritableUnalignedChunk());
}
- if (MetaspaceImpl.isSupported()) {
+ if (Metaspace.isSupported()) {
success &= MetaspaceImpl.singleton().verifyRememberedSets();
}
diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialAndEpsilonGCOptions.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialAndEpsilonGCOptions.java
index a09f1b06db0c..a80303db2de1 100644
--- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialAndEpsilonGCOptions.java
+++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialAndEpsilonGCOptions.java
@@ -27,15 +27,21 @@
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.RegisterForIsolateArgumentParser;
import com.oracle.svm.core.SubstrateOptions;
+import com.oracle.svm.core.metaspace.Metaspace;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.option.NotifyGCRuntimeOptionKey;
import com.oracle.svm.core.option.RuntimeOptionKey;
import com.oracle.svm.core.util.UserError;
+import jdk.graal.compiler.api.replacements.Fold;
+import jdk.graal.compiler.core.common.NumUtil;
import jdk.graal.compiler.options.Option;
import jdk.graal.compiler.options.OptionType;
-/** Common options that can be specified for both the serial and the epsilon GC. */
+/**
+ * Common options that can be specified for both the serial and the epsilon GC. Some of these
+ * options are validated at build-time in {@link HeapParameters#initialize}.
+ */
public final class SerialAndEpsilonGCOptions {
@Option(help = "The maximum heap size as percent of physical memory. Serial and epsilon GC only.", type = OptionType.User) //
public static final RuntimeOptionKey MaximumHeapSizePercent = new NotifyGCRuntimeOptionKey<>(80, SerialAndEpsilonGCOptions::validateSerialOrEpsilonRuntimeOption);
@@ -71,6 +77,42 @@ public final class SerialAndEpsilonGCOptions {
@Option(help = "Print information about TLABs. Printed when The TLABs are retired before a GC, and during the resizing of the TLABs. Serial and epsilon GC only.", type = OptionType.Expert)//
public static final HostedOptionKey PrintTLAB = new HostedOptionKey<>(false, SerialAndEpsilonGCOptions::validateSerialOrEpsilonHostedOption);
+ /** Query these options only through an appropriate method. */
+ public static class ConcealedOptions {
+ /** Use {@link #getReservedMetaspaceSize} instead. */
+ @Option(help = "Determines the maximum size in bytes of the metaspace. 0 means set ergonomically.")//
+ public static final HostedOptionKey MaxMetaspaceSize = new HostedOptionKey<>(0, SerialAndEpsilonGCOptions::validateSerialOrEpsilonHostedOption);
+ }
+
+ @Fold
+ public static int getNullRegionSize() {
+ if (SubstrateOptions.SpawnIsolates.getValue() && SubstrateOptions.UseNullRegion.getValue()) {
+ /*
+ * The image heap will be mapped in a way that there is a memory protected gap between
+ * 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(SerialAndEpsilonGCOptions.AlignedHeapChunkSize.getValue());
+ }
+ return 0;
+ }
+
+ @Fold
+ public static int getReservedMetaspaceSize() {
+ if (!Metaspace.isSupported()) {
+ return 0;
+ }
+
+ int value = SerialAndEpsilonGCOptions.ConcealedOptions.MaxMetaspaceSize.getValue();
+ if (value != 0) {
+ return value;
+ }
+
+ /* Use roughly 32 MB as the default. */
+ long result = NumUtil.roundUp(32L * 1024 * 1024, SerialAndEpsilonGCOptions.AlignedHeapChunkSize.getValue());
+ return NumUtil.safeToInt(result);
+ }
+
private SerialAndEpsilonGCOptions() {
}
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 6ba052cbadc8..859d6c1904b8 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
@@ -102,7 +102,8 @@ public class BarrierSnippets extends SubstrateTemplates implements Snippets {
private final SnippetInfo postWriteBarrierSnippet;
private final SnippetInfo arrayRangePostWriteBarrierSnippet;
- BarrierSnippets(OptionValues options, Providers providers) {
+ @SuppressWarnings("this-escape")
+ public BarrierSnippets(OptionValues options, Providers providers) {
super(options, providers);
this.postWriteBarrierSnippet = snippet(providers, BarrierSnippets.class, "postWriteBarrierSnippet", CARD_REMEMBERED_SET_LOCATION);
diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/metaspace/MetaspaceImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/metaspace/MetaspaceImpl.java
index be663e403553..1f2ba0cc50ab 100644
--- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/metaspace/MetaspaceImpl.java
+++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/metaspace/MetaspaceImpl.java
@@ -32,16 +32,19 @@
import org.graalvm.word.Pointer;
import com.oracle.svm.core.Uninterruptible;
+import com.oracle.svm.core.genscavenge.AddressRangeCommittedMemoryProvider;
import com.oracle.svm.core.genscavenge.HeapVerifier;
import com.oracle.svm.core.genscavenge.OldGeneration;
import com.oracle.svm.core.genscavenge.Space;
import com.oracle.svm.core.genscavenge.remset.FirstObjectTable;
import com.oracle.svm.core.genscavenge.remset.RememberedSet;
+import com.oracle.svm.core.heap.ObjectVisitor;
import com.oracle.svm.core.heap.UninterruptibleObjectReferenceVisitor;
import com.oracle.svm.core.heap.UninterruptibleObjectVisitor;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.metaspace.Metaspace;
+import com.oracle.svm.core.os.CommittedMemoryProvider;
import com.oracle.svm.core.thread.VMOperation;
import jdk.graal.compiler.api.replacements.Fold;
@@ -64,14 +67,9 @@ public class MetaspaceImpl implements Metaspace {
public MetaspaceImpl() {
}
- @Fold
- public static boolean isSupported() {
- return ImageSingletons.contains(MetaspaceImpl.class);
- }
-
@Fold
public static MetaspaceImpl singleton() {
- return ImageSingletons.lookup(MetaspaceImpl.class);
+ return (MetaspaceImpl) ImageSingletons.lookup(Metaspace.class);
}
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
@@ -100,7 +98,12 @@ public boolean isInAddressSpace(Object obj) {
@Override
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
public boolean isInAddressSpace(Pointer ptr) {
- /* Replace with address range check once GR-60085 is implemented. */
+ CommittedMemoryProvider memoryProvider = ImageSingletons.lookup(CommittedMemoryProvider.class);
+ if (memoryProvider instanceof AddressRangeCommittedMemoryProvider p) {
+ return p.isInMetaspace(ptr);
+ }
+
+ /* Metaspace does not have a contiguous address space. */
return isInAllocatedMemory(ptr);
}
@@ -114,6 +117,12 @@ public byte[] allocateByteArray(int length) {
return allocator.allocateByteArray(length);
}
+ @Override
+ public void walkObjects(ObjectVisitor visitor) {
+ assert VMOperation.isInProgress() : "prevent other threads from manipulating the metaspace";
+ space.walkObjects(visitor);
+ }
+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
public void walkObjects(UninterruptibleObjectVisitor objectVisitor) {
assert VMOperation.isInProgress() : "prevent other threads from manipulating the metaspace";
diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxImageHeapProvider.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxImageHeapProvider.java
index c6288ae65daa..e782c762325b 100644
--- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxImageHeapProvider.java
+++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxImageHeapProvider.java
@@ -46,7 +46,6 @@
import static com.oracle.svm.core.imagelayer.ImageLayerSection.SectionEntries.VARIABLY_SIZED_DATA;
import static com.oracle.svm.core.posix.linux.ProcFSSupport.findMapping;
import static com.oracle.svm.core.util.PointerUtils.roundDown;
-import static com.oracle.svm.core.util.UnsignedUtils.isAMultiple;
import static com.oracle.svm.core.util.UnsignedUtils.roundUp;
import static jdk.graal.compiler.word.Word.signed;
import static jdk.graal.compiler.word.Word.unsigned;
@@ -123,12 +122,6 @@ public class LinuxImageHeapProvider extends AbstractImageHeapProvider {
private static final int MAX_PATHLEN = 4096;
- /**
- * Used for caching heap address space size when using layered images. Within layered images
- * calculating this value requires iterating through multiple sections.
- */
- static final CGlobalData CACHED_LAYERED_IMAGE_HEAP_ADDRESS_SPACE_SIZE = CGlobalDataFactory.createWord();
-
private static final class ImageHeapPatchingState {
static final Word UNINITIALIZED = Word.zero();
static final Word IN_PROGRESS = Word.unsigned(1);
@@ -137,64 +130,17 @@ private static final class ImageHeapPatchingState {
private static final CGlobalData IMAGE_HEAP_PATCHING_STATE = CGlobalDataFactory.createWord(ImageHeapPatchingState.UNINITIALIZED);
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
- private static UnsignedWord getLayeredImageHeapAddressSpaceSize() {
- // check if value is cached
- Word currentValue = CACHED_LAYERED_IMAGE_HEAP_ADDRESS_SPACE_SIZE.get().read();
- if (currentValue.isNonNull()) {
- return currentValue;
- }
- int imageHeapOffset = Heap.getHeap().getImageHeapOffsetInAddressSpace();
- assert imageHeapOffset >= 0;
- UnsignedWord size = Word.unsigned(imageHeapOffset);
- UnsignedWord granularity = VirtualMemoryProvider.get().getGranularity();
- assert isAMultiple(size, granularity);
-
- /*
- * Walk through the sections and add up the layer image heap sizes.
- */
-
- Pointer currentSection = ImageLayerSection.getInitialLayerSection().get();
- while (currentSection.isNonNull()) {
- Word heapBegin = currentSection.readWord(ImageLayerSection.getEntryOffset(HEAP_BEGIN));
- Word heapEnd = currentSection.readWord(ImageLayerSection.getEntryOffset(HEAP_END));
- size = size.add(getImageHeapSizeInFile(heapBegin, heapEnd));
- size = roundUp(size, granularity);
- currentSection = currentSection.readWord(ImageLayerSection.getEntryOffset(NEXT_SECTION));
- }
-
- // cache the value
- CACHED_LAYERED_IMAGE_HEAP_ADDRESS_SPACE_SIZE.get().write(size);
- return size;
- }
-
- @Override
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
- public UnsignedWord getImageHeapAddressSpaceSize() {
- if (ImageLayerBuildingSupport.buildingImageLayer()) {
- return getLayeredImageHeapAddressSpaceSize();
- }
- return super.getImageHeapAddressSpaceSize();
- }
-
@Uninterruptible(reason = "Called during isolate initialization.")
- protected int initializeLayeredImage(Pointer firstHeapStart, Pointer selfReservedHeapBase, UnsignedWord initialRemainingSize, WordPointer endPointer) {
- int result = -1;
- UnsignedWord remainingSize = initialRemainingSize;
+ protected int initializeLayeredImage(Pointer imageHeapStart, Pointer imageHeapEnd, Pointer selfReservedHeapBase) {
+ UnsignedWord imageHeapAlignment = unsigned(Heap.getHeap().getImageHeapAlignment());
+ assert PointerUtils.isAMultiple(imageHeapStart, imageHeapAlignment);
patchLayeredImageHeap();
+ int result = -1;
int layerCount = 0;
Pointer currentSection = ImageLayerSection.getInitialLayerSection().get();
- Pointer currentHeapStart = firstHeapStart;
- WordPointer curEndPointer = endPointer;
- if (endPointer.isNull()) {
- /*
- * When endPointer is null, we still need to track it locally to compute the next heap
- * starting location.
- */
- curEndPointer = StackValue.get(WordPointer.class);
- }
+ Pointer currentHeapStart = imageHeapStart;
while (currentSection.isNonNull()) {
var cachedFDPointer = ImageLayerSection.getCachedImageFDs().get().addressOf(layerCount);
var cachedOffsetsPointer = ImageLayerSection.getCachedImageHeapOffsets().get().addressOf(layerCount);
@@ -210,7 +156,11 @@ protected int initializeLayeredImage(Pointer firstHeapStart, Pointer selfReserve
Word heapWritablePatchedBegin = currentSection.readWord(ImageLayerSection.getEntryOffset(HEAP_WRITEABLE_PATCHED_BEGIN));
Word heapWritablePatchedEnd = currentSection.readWord(ImageLayerSection.getEntryOffset(HEAP_WRITEABLE_PATCHED_END));
- result = initializeImageHeap(currentHeapStart, remainingSize, curEndPointer,
+ /* Each layer's image heap starts at an aligned offset. */
+ currentHeapStart = PointerUtils.roundUp(currentHeapStart, imageHeapAlignment);
+
+ UnsignedWord imageHeapSize = getImageHeapSizeInFile(heapBegin, heapEnd);
+ result = initializeImageHeap(currentHeapStart, imageHeapSize,
cachedFDPointer, cachedOffsetsPointer, cachedImageHeapRelocationsPtr, MAGIC.get(),
heapBegin, heapEnd,
heapRelocBegin, heapAnyRelocPointer, heapRelocEnd,
@@ -219,14 +169,14 @@ protected int initializeLayeredImage(Pointer firstHeapStart, Pointer selfReserve
freeImageHeap(selfReservedHeapBase);
return result;
}
- Pointer newHeapStart = curEndPointer.read(); // aligned
- remainingSize = remainingSize.subtract(newHeapStart.subtract(currentHeapStart));
- currentHeapStart = newHeapStart;
+
+ currentHeapStart = currentHeapStart.add(imageHeapSize);
// read the next layer
currentSection = currentSection.readWord(ImageLayerSection.getEntryOffset(NEXT_SECTION));
layerCount++;
}
+ assert imageHeapEnd == currentHeapStart;
return result;
}
@@ -353,11 +303,11 @@ private static void applyLayerImageHeapRefPatches(Pointer patches, Pointer layer
@Override
@Uninterruptible(reason = "Called during isolate initialization.")
- public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, WordPointer basePointer, WordPointer endPointer) {
+ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, WordPointer heapBaseOut, WordPointer imageHeapEndOut) {
Pointer selfReservedMemory = Word.nullPointer();
UnsignedWord requiredSize = getTotalRequiredAddressSpaceSize();
if (reservedAddressSpace.isNull()) {
- UnsignedWord alignment = Word.unsigned(Heap.getHeap().getPreferredAddressSpaceAlignment());
+ UnsignedWord alignment = Word.unsigned(Heap.getHeap().getHeapBaseAlignment());
selfReservedMemory = VirtualMemoryProvider.get().reserve(requiredSize, alignment, false);
if (selfReservedMemory.isNull()) {
return CEntryPointErrors.RESERVE_ADDRESS_SPACE_FAILED;
@@ -365,7 +315,6 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W
} else if (reservedSize.belowThan(requiredSize)) {
return CEntryPointErrors.INSUFFICIENT_ADDRESS_SPACE;
}
- UnsignedWord remainingSize = requiredSize;
Pointer heapBase;
Pointer selfReservedHeapBase;
@@ -378,7 +327,6 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W
heapBase = reservedAddressSpace.add(preHeapRequiredBytes);
selfReservedHeapBase = Word.nullPointer();
}
- remainingSize = remainingSize.subtract(preHeapRequiredBytes);
int error = DynamicMethodAddressResolutionHeapSupport.get().initialize();
if (error != CEntryPointErrors.NO_ERROR) {
@@ -396,14 +344,20 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W
selfReservedHeapBase = selfReservedMemory;
}
- int imageHeapOffsetInAddressSpace = Heap.getHeap().getImageHeapOffsetInAddressSpace();
- basePointer.write(heapBase);
- Pointer imageHeapStart = heapBase.add(imageHeapOffsetInAddressSpace);
- remainingSize = remainingSize.subtract(imageHeapOffsetInAddressSpace);
+ /* Update heap base and image heap end. */
+ assert PointerUtils.isAMultiple(heapBase, Word.unsigned(Heap.getHeap().getHeapBaseAlignment()));
+ heapBaseOut.write(heapBase);
+
+ Pointer imageHeapEnd = getImageHeapEnd(heapBase);
+ assert PointerUtils.isAMultiple(imageHeapEnd, VirtualMemoryProvider.get().getGranularity());
+ imageHeapEndOut.write(imageHeapEnd);
+
+ /* Map the image heap. */
+ Pointer imageHeapStart = getImageHeapBegin(heapBase);
if (ImageLayerBuildingSupport.buildingImageLayer()) {
- return initializeLayeredImage(imageHeapStart, selfReservedHeapBase, remainingSize, endPointer);
+ return initializeLayeredImage(imageHeapStart, imageHeapEnd, selfReservedHeapBase);
} else {
- int result = initializeImageHeap(imageHeapStart, remainingSize, endPointer,
+ int result = initializeImageHeap(imageHeapStart, getImageHeapSizeInFile(),
CACHED_IMAGE_FD.get(), CACHED_IMAGE_HEAP_OFFSET.get(), CACHED_IMAGE_HEAP_RELOCATIONS.get(), MAGIC.get(),
IMAGE_HEAP_BEGIN.get(), IMAGE_HEAP_END.get(),
IMAGE_HEAP_RELOCATABLE_BEGIN.get(), IMAGE_HEAP_A_RELOCATABLE_POINTER.get(), IMAGE_HEAP_RELOCATABLE_END.get(),
@@ -416,8 +370,7 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W
}
@Uninterruptible(reason = "Called during isolate initialization.")
- private static int initializeImageHeap(Pointer imageHeap, UnsignedWord reservedSize, WordPointer endPointer, WordPointer cachedFd, WordPointer cachedOffsetInFile,
- WordPointer cachedImageHeapRelocationsPtr,
+ private static int initializeImageHeap(Pointer imageHeap, UnsignedWord imageHeapSize, WordPointer cachedFd, WordPointer cachedOffsetInFile, WordPointer cachedImageHeapRelocationsPtr,
Pointer magicAddress, Word heapBeginSym, Word heapEndSym, Word heapRelocsSym, Pointer heapAnyRelocPointer, Word heapRelocsEndSym, Word heapWritablePatchedSym,
Word heapWritablePatchedEndSym, Word heapWritableSym, Word heapWritableEndSym) {
assert heapBeginSym.belowOrEqual(heapWritableSym) && heapWritableSym.belowOrEqual(heapWritableEndSym) && heapWritableEndSym.belowOrEqual(heapEndSym);
@@ -445,17 +398,11 @@ private static int initializeImageHeap(Pointer imageHeap, UnsignedWord reservedS
}
}
- UnsignedWord pageSize = VirtualMemoryProvider.get().getGranularity();
- UnsignedWord imageHeapSize = getImageHeapSizeInFile(heapBeginSym, heapEndSym);
- assert reservedSize.aboveOrEqual(imageHeapSize);
- if (endPointer.isNonNull()) {
- endPointer.write(roundUp(imageHeap.add(imageHeapSize), pageSize));
- }
-
/*
* If we cannot find or open the image file, fall back to copy it from memory (the image
* heap must be in pristine condition for that).
*/
+ UnsignedWord pageSize = VirtualMemoryProvider.get().getGranularity();
if (fd.equal(CANNOT_OPEN_FD)) {
int result = initializeImageHeapWithMremap(imageHeap, imageHeapSize, pageSize, cachedImageHeapRelocationsPtr, heapBeginSym, heapRelocsSym, heapAnyRelocPointer, heapRelocsEndSym,
heapWritablePatchedSym, heapWritablePatchedEndSym, heapWritableSym, heapWritableEndSym);
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java
index ed7da2af7dcb..d16e2d7b941f 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java
@@ -813,7 +813,6 @@ private static int verifyIsolateThread(IsolateThread thread) {
if (!VMThreads.singleton().verifyIsCurrentThread(thread) || !VMThreads.isAttached(thread)) {
throw VMError.shouldNotReachHere("A call from native code to Java code provided the wrong JNI environment or the wrong IsolateThread. " +
"The JNI environment / IsolateThread is a thread-local data structure and must not be shared between threads.");
-
}
return CEntryPointErrors.NO_ERROR;
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java
index b911f1919ad0..bee3d9efc051 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java
@@ -36,6 +36,7 @@
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;
+import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.hub.PredefinedClassesSupport;
@@ -47,8 +48,6 @@
import jdk.graal.compiler.api.replacements.Fold;
public abstract class Heap {
- protected long startOffset;
-
@Fold
public static Heap getHeap() {
return ImageSingletons.lookup(Heap.class);
@@ -112,7 +111,7 @@ protected Heap() {
/** Visits all loaded classes in the heap (see {@link PredefinedClassesSupport}). */
public void visitLoadedClasses(Consumer> visitor) {
- for (Class> clazz : getAllClasses()) {
+ for (Class> clazz : getClassesInImageHeap()) {
if (DynamicHub.fromClass(clazz).isLoaded()) {
visitor.accept(clazz);
}
@@ -120,10 +119,10 @@ public void visitLoadedClasses(Consumer> visitor) {
}
/**
- * Get all known classes. Intentionally protected to prevent access to classes that have not
- * been "loaded" yet, see {@link PredefinedClassesSupport}.
+ * Returns all class objects that live in the image heap. Intentionally protected to prevent
+ * access to classes that have not been loaded yet, see {@link PredefinedClassesSupport}.
*/
- protected abstract List> getAllClasses();
+ protected abstract List> getClassesInImageHeap();
/**
* Get the ObjectHeader implementation that this Heap uses.
@@ -141,9 +140,27 @@ public void visitLoadedClasses(Consumer> visitor) {
/** Reset the heap to the normal execution state. */
public abstract void endSafepoint();
- /** Returns a multiple to which the heap address space should be aligned to at runtime. */
+ /* Only used by legacy code, see GR-68813. */
+ @Fold
+ @Deprecated
+ public int getPreferredAddressSpaceAlignment() {
+ return getHeapBaseAlignment();
+ }
+
+ /**
+ * Returns the alignment in bytes that the heap base must adhere to at runtime. Note that this
+ * alignment is not enforced if {@link SubstrateOptions#SpawnIsolates} is disabled.
+ */
+ @Fold
+ public abstract int getHeapBaseAlignment();
+
+ /**
+ * Returns the alignment in bytes that each image heap and any auxiliary images must adhere to
+ * at runtime. Note that this alignment is not enforced if
+ * {@link SubstrateOptions#SpawnIsolates} is disabled.
+ */
@Fold
- public abstract int getPreferredAddressSpaceAlignment();
+ public abstract int getImageHeapAlignment();
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public Pointer getImageHeapStart() {
@@ -152,7 +169,7 @@ public Pointer getImageHeapStart() {
/**
* Returns an offset relative to the heap base, at which the image heap should be mapped into
- * the address space.
+ * the address space. The offset is a multiple of {@link #getImageHeapAlignment}.
*/
@Fold
public abstract int getImageHeapOffsetInAddressSpace();
@@ -235,10 +252,6 @@ public Pointer getImageHeapStart() {
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public abstract UnsignedWord getUsedMemoryAfterLastGC();
- public abstract UnsignedWord getImageHeapReservedBytes();
-
- public abstract UnsignedWord getImageHeapCommittedBytes();
-
/** Consider all references in the given object as needing remembered set entries. */
@Uninterruptible(reason = "Ensure that no GC can occur between modification of the object and this call.", callerMustBe = true)
public abstract void dirtyAllReferencesOf(Object obj);
@@ -258,11 +271,4 @@ public Pointer getImageHeapStart() {
*/
@Uninterruptible(reason = "Ensure that no GC can occur between this call and usage of the salt.", callerMustBe = true)
public abstract long getIdentityHashSalt(Object obj);
-
- /**
- * Sets the start offset of the heap.
- */
- public void setStartOffset(long startOffset) {
- this.startOffset = startOffset;
- }
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/OutOfMemoryUtil.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/OutOfMemoryUtil.java
index ef564b85f264..3acd94a3f8ab 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/OutOfMemoryUtil.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/OutOfMemoryUtil.java
@@ -38,8 +38,8 @@
/**
* This class must be used for {@link OutOfMemoryError}s that are thrown because the VM is out of
- * Java heap memory. Other {@link OutOfMemoryError}s (e.g., when we run out of native memory) can be
- * thrown directly.
+ * Java heap memory. Other {@link OutOfMemoryError}s (e.g., when we run out of native memory) should
+ * be thrown directly.
*/
public class OutOfMemoryUtil {
private static final OutOfMemoryError OUT_OF_MEMORY_ERROR = new OutOfMemoryError("Garbage-collected heap size exceeded. Consider increasing the maximum Java heap size, for example with '-Xmx'.");
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpMetadata.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpMetadata.java
index 30f847536fb5..06004ea3d84d 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpMetadata.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpMetadata.java
@@ -46,11 +46,13 @@
import com.oracle.svm.core.heap.ObjectVisitor;
import com.oracle.svm.core.heap.UnknownObjectField;
import com.oracle.svm.core.hub.DynamicHub;
+import com.oracle.svm.core.hub.registry.TypeIDs;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
import com.oracle.svm.core.memory.NullableNativeMemory;
+import com.oracle.svm.core.metaspace.Metaspace;
import com.oracle.svm.core.nmt.NmtCategory;
import com.oracle.svm.core.traits.BuiltinTraits.RuntimeAccessOnly;
import com.oracle.svm.core.traits.BuiltinTraits.SingleLayer;
@@ -91,7 +93,7 @@
* |----------------------------|
* | information per field |
* |----------------------------|
- * | u1 type |
+ * | u1 type |
* | uv fieldNameIndex |
* | uv location |
* |----------------------------|
@@ -132,7 +134,6 @@ public boolean initialize() {
int totalFieldCount = 0;
int totalFieldNameCount = 0;
- int maxTypeId = Integer.MIN_VALUE;
/*
* First read all encoded data arrays to determine how large of data structures to allocate.
@@ -147,10 +148,10 @@ public boolean initialize() {
totalFieldCount += NativeCoder.readInt(stream);
NativeCoder.readInt(stream); // class count
totalFieldNameCount += NativeCoder.readInt(stream);
- maxTypeId = Integer.max(Pack200Coder.readUVAsInt(stream), maxTypeId);
}
fieldNameCount = totalFieldNameCount;
- classInfoCount = maxTypeId + 1;
+ /* Allocating a contiguous array may be a problem with class unloading, see GR-68380. */
+ classInfoCount = TypeIDs.singleton().getNumTypeIds();
/*
* Precompute a few small data structures so that the heap dumping code can access the
@@ -190,7 +191,6 @@ public boolean initialize() {
NativeCoder.readInt(stream); // field count
int classCount = NativeCoder.readInt(stream);
int currentFieldNameCount = NativeCoder.readInt(stream);
- Pack200Coder.readUVAsInt(stream); // maxTypeId
/* Read the classes and fields. */
int fieldIndex = 0;
@@ -237,6 +237,19 @@ public boolean initialize() {
/* Store the DynamicHubs in their corresponding ClassInfo structs. */
computeHubDataVisitor.initialize();
Heap.getHeap().walkImageHeapObjects(computeHubDataVisitor);
+ Metaspace.singleton().walkObjects(computeHubDataVisitor);
+
+ /*
+ * Classes that are loaded at runtime don't have any declared fields at the moment. This
+ * needs to be changed once GR-60069 is merged.
+ */
+ for (int i = TypeIDs.singleton().getFirstRuntimeTypeId(); i < classInfoCount; i++) {
+ ClassInfo classInfo = getClassInfo(i);
+ if (ClassInfoAccess.isValid(classInfo)) {
+ classInfo.setStaticFieldCount(0);
+ classInfo.setInstanceFieldCount(0);
+ }
+ }
/* Compute the size of the instance fields per class. */
for (int i = 0; i < classInfoCount; i++) {
@@ -471,7 +484,7 @@ public void initialize() {
@Override
public void visitObject(Object o) {
- if (o instanceof DynamicHub hub) {
+ if (o instanceof DynamicHub hub && hub.isLoaded()) {
ClassInfo classInfo = HeapDumpMetadata.singleton().getClassInfo(hub.getTypeID());
assert classInfo.getHub() == null;
classInfo.setHub(hub);
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpWriter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpWriter.java
index a80afd6f2db0..1044df9d7df7 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpWriter.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpWriter.java
@@ -75,6 +75,7 @@
import com.oracle.svm.core.jdk.UninterruptibleUtils.ReplaceDotWithSlash;
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
import com.oracle.svm.core.log.Log;
+import com.oracle.svm.core.metaspace.Metaspace;
import com.oracle.svm.core.nmt.NmtCategory;
import com.oracle.svm.core.os.BufferedFileOperationSupport;
import com.oracle.svm.core.os.BufferedFileOperationSupport.BufferedFile;
@@ -584,17 +585,21 @@ private void writeLoadedClasses() {
if (ClassInfoAccess.isValid(classInfo)) {
DynamicHub hub = classInfo.getHub();
if (hub.isLoaded()) {
- startTopLevelRecord(HProfTopLevelRecord.LOAD_CLASS);
- writeInt(classInfo.getSerialNum());
- writeClassId(hub);
- writeInt(DUMMY_STACK_TRACE_ID);
- writeObjectId(hub.getName());
- endTopLevelRecord();
+ writeLoadedClass(classInfo, hub);
}
}
}
}
+ private void writeLoadedClass(ClassInfo classInfo, DynamicHub hub) {
+ startTopLevelRecord(HProfTopLevelRecord.LOAD_CLASS);
+ writeInt(classInfo.getSerialNum());
+ writeClassId(hub);
+ writeInt(DUMMY_STACK_TRACE_ID);
+ writeObjectId(hub.getName());
+ endTopLevelRecord();
+ }
+
private void writeStackTraces(Pointer currentThreadSp) {
writeDummyStackTrace();
@@ -772,6 +777,9 @@ private void writeObjects() {
dumpObjectsVisitor.initialize(largeObjects);
Heap.getHeap().walkImageHeapObjects(dumpObjectsVisitor);
+ dumpObjectsVisitor.initialize(largeObjects);
+ Metaspace.singleton().walkObjects(dumpObjectsVisitor);
+
dumpObjectsVisitor.initialize(largeObjects);
Heap.getHeap().walkCollectedHeapObjects(dumpObjectsVisitor);
@@ -836,8 +844,9 @@ private void writeObject(Object obj) {
writeInstance(obj);
}
- if (Heap.getHeap().isInImageHeap(obj)) {
- markImageHeapObjectAsGCRoot(obj);
+ if (Heap.getHeap().isInImageHeap(obj) || Metaspace.singleton().isInAddressSpace(obj)) {
+ /* Image heap and metaspace objects are marked as GC_ROOT_JNI_GLOBAL. */
+ markAsJniGlobalGCRoot(obj);
}
/*
@@ -862,12 +871,6 @@ private void markMonitorAsGCRoot(Object monitor) {
endSubRecord(recordSize);
}
- /** We mark image heap objects as GC_ROOT_JNI_GLOBAL. */
- private void markImageHeapObjectAsGCRoot(Object obj) {
- assert Heap.getHeap().isInImageHeap(obj);
- markAsJniGlobalGCRoot(obj);
- }
-
private void markAsJniGlobalGCRoot(Object obj) {
int recordSize = 1 + 2 * wordSize();
startSubRecord(HProfSubRecord.GC_ROOT_JNI_GLOBAL, recordSize);
@@ -1363,15 +1366,18 @@ public void initialize(GrowableWordArray largeObjects) {
@Override
@RestrictHeapAccess(access = NO_ALLOCATION, reason = "Heap dumping must not allocate.")
public void visitObject(Object obj) {
- if (!isFillerObject(obj)) {
- if (isLarge(obj)) {
- boolean added = GrowableWordArrayAccess.add(largeObjects, Word.objectToUntrackedPointer(obj), NmtCategory.HeapDump);
- if (!added) {
- Log.log().string("Failed to add an element to the large object list. Heap dump will be incomplete.").newline();
- }
- } else {
- writeObject(obj);
+ if (isFillerObject(obj)) {
+ /* Skip filler objects as they are irrelevant and only make the heap dump larger. */
+ return;
+ }
+
+ if (isLarge(obj)) {
+ boolean added = GrowableWordArrayAccess.add(largeObjects, Word.objectToUntrackedPointer(obj), NmtCategory.HeapDump);
+ if (!added) {
+ Log.log().string("Failed to add an element to the large object list. Heap dump will be incomplete.").newline();
}
+ } else {
+ writeObject(obj);
}
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java
index a5af183a972e..5213f0d6f158 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java
@@ -142,6 +142,8 @@
import jdk.graal.compiler.api.directives.GraalDirectives;
import jdk.graal.compiler.core.common.NumUtil;
import jdk.graal.compiler.core.common.SuppressFBWarnings;
+import jdk.graal.compiler.nodes.extended.MembarNode;
+import jdk.graal.compiler.nodes.extended.MembarNode.FenceKind;
import jdk.graal.compiler.nodes.java.FinalFieldBarrierNode;
import jdk.graal.compiler.replacements.ReplacementsUtil;
import jdk.internal.access.JavaLangReflectAccess;
@@ -578,10 +580,25 @@ public static DynamicHub allocate(String name, DynamicHub superHub, Object inter
// skip vtable (special treatment)
- writeObject(hub, dynamicHubOffsets.getCompanionOffset(), companion);
+ return finishInitialization(hub, companion);
+ }
- FinalFieldBarrierNode.finalFieldBarrier(hub);
+ /**
+ * The {@link #companion} field must be assigned last during initialization, as it determines
+ * when a class is regarded as loaded (see {@link #isLoaded()}). Once {@code isLoaded()} returns
+ * {@code true}, it is essential that other threads observe consistent and fully initialized
+ * values for every field (ensured by the {@link FenceKind#STORE_STORE} barrier).
+ *
+ * This guarantee is particularly important for scenarios where code may access newly-allocated
+ * {@code DynamicHub}s that might not be fully initialized yet (for example, during heap
+ * dumping).
+ */
+ private static DynamicHub finishInitialization(DynamicHub hub, DynamicHubCompanion companion) {
+ MembarNode.memoryBarrier(FenceKind.STORE_STORE);
+ writeObject(hub, DynamicHubOffsets.singleton().getCompanionOffset(), companion);
+ /* Emit a final field barrier as if we executed a normal constructor. */
+ FinalFieldBarrierNode.finalFieldBarrier(hub);
return hub;
}
@@ -1138,7 +1155,7 @@ public ClassLoader getClassLoader() {
private native ClassLoader getClassLoader0();
public boolean isLoaded() {
- return companion.classLoader != NO_CLASS_LOADER;
+ return companion != null && companion.classLoader != NO_CLASS_LOADER;
}
void setClassLoaderAtRuntime(ClassLoader loader) {
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java
index 931e9d67cb37..c3bc80738ee5 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java
@@ -353,7 +353,7 @@ private Class> createClass(ParserKlass parsed, ClassDefinitionInfo info, Symbo
* @formatter:on
*/
DynamicHub superHub = DynamicHub.fromClass(superClass);
- int typeID = ClassRegistries.nextTypeId();
+ int typeID = TypeIDs.singleton().nextTypeId();
short numInterfacesTypes = (short) transitiveSuperInterfaces.size();
short numClassTypes;
short typeIDDepth;
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java
index 8f03148c75cc..ddd9e85ad67c 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java
@@ -28,7 +28,6 @@
import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import org.graalvm.collections.EconomicMap;
@@ -92,8 +91,6 @@ public final class ClassRegistries implements ParsingContext {
private final AbstractClassRegistry bootRegistry;
private final EconomicMap bootPackageToModule;
- private final AtomicInteger nextTypeId = new AtomicInteger(0);
-
@Platforms(Platform.HOSTED_ONLY.class)
public ClassRegistries() {
if (RuntimeClassLoading.isSupported()) {
@@ -138,17 +135,6 @@ public static String[] getSystemPackageNames() {
return result;
}
- @Platforms(Platform.HOSTED_ONLY.class)
- public static void setStartingTypeId(int id) {
- singleton().nextTypeId.set(id);
- }
-
- public static int nextTypeId() {
- int nextTypeId = singleton().nextTypeId.getAndIncrement();
- VMError.guarantee(nextTypeId > 0);
- return nextTypeId;
- }
-
public static Class> findBootstrapClass(String name) {
try {
return singleton().resolve(name, null);
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/TypeIDs.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/TypeIDs.java
new file mode 100644
index 000000000000..1efeed608c90
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/TypeIDs.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.core.hub.registry;
+
+import java.util.EnumSet;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.Platform;
+import org.graalvm.nativeimage.Platforms;
+
+import com.oracle.svm.core.BuildPhaseProvider.AfterCompilation;
+import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
+import com.oracle.svm.core.feature.InternalFeature;
+import com.oracle.svm.core.heap.UnknownPrimitiveField;
+import com.oracle.svm.core.hub.DynamicHub;
+import com.oracle.svm.core.hub.DynamicHubSupport;
+import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
+import com.oracle.svm.core.layeredimagesingleton.ApplicationLayerOnlyImageSingleton;
+import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
+import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
+import com.oracle.svm.core.util.VMError;
+
+/** Keeps track of type ID information at run-time (see {@link DynamicHub#getTypeID()}). */
+public class TypeIDs implements ApplicationLayerOnlyImageSingleton, UnsavedSingleton {
+ private final AtomicInteger nextTypeId = new AtomicInteger();
+ @UnknownPrimitiveField(availability = AfterCompilation.class) //
+ private int firstRuntimeTypeId;
+
+ @Platforms(Platform.HOSTED_ONLY.class)
+ TypeIDs() {
+ }
+
+ public static TypeIDs singleton() {
+ return ImageSingletons.lookup(TypeIDs.class);
+ }
+
+ @Platforms(Platform.HOSTED_ONLY.class)
+ void initialize() {
+ assert firstRuntimeTypeId == 0 && nextTypeId.get() == 0;
+ firstRuntimeTypeId = DynamicHubSupport.currentLayer().getMaxTypeId() + 1;
+ nextTypeId.set(firstRuntimeTypeId);
+ }
+
+ /** The type id that is used for the first type that is loaded at run-time. */
+ public int getFirstRuntimeTypeId() {
+ assert firstRuntimeTypeId > 0;
+ return firstRuntimeTypeId;
+ }
+
+ public int nextTypeId() {
+ int result = nextTypeId.getAndIncrement();
+ VMError.guarantee(result > 0);
+ return result;
+ }
+
+ public int getNumTypeIds() {
+ return nextTypeId.get();
+ }
+
+ @Override
+ public EnumSet getImageBuilderFlags() {
+ return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
+ }
+}
+
+@AutomaticallyRegisteredFeature
+class TypeIdsFeature implements InternalFeature {
+ @Override
+ public boolean isInConfiguration(IsInConfigurationAccess access) {
+ return ImageLayerBuildingSupport.lastImageBuild();
+ }
+
+ @Override
+ public void duringSetup(DuringSetupAccess access) {
+ ImageSingletons.add(TypeIDs.class, new TypeIDs());
+ }
+
+ @Override
+ public void afterCompilation(AfterCompilationAccess access) {
+ TypeIDs.singleton().initialize();
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeapLayoutInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeapLayoutInfo.java
index 4af8d72bbd23..a0558a64a8f2 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeapLayoutInfo.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/ImageHeapLayoutInfo.java
@@ -25,6 +25,8 @@
package com.oracle.svm.core.image;
import com.oracle.svm.core.BuildPhaseProvider.AfterHeapLayout;
+import com.oracle.svm.core.SubstrateOptions;
+import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.heap.UnknownPrimitiveField;
import jdk.graal.compiler.debug.Assertions;
@@ -32,7 +34,7 @@
/** Layout offsets and sizes. All offsets are relative to the heap base. */
public class ImageHeapLayoutInfo {
@UnknownPrimitiveField(availability = AfterHeapLayout.class) private final long startOffset;
- @UnknownPrimitiveField(availability = AfterHeapLayout.class) private final long imageHeapSize;
+ @UnknownPrimitiveField(availability = AfterHeapLayout.class) private final long endOffset;
@UnknownPrimitiveField(availability = AfterHeapLayout.class) private final long writableOffset;
@UnknownPrimitiveField(availability = AfterHeapLayout.class) private final long writableSize;
@@ -43,10 +45,11 @@ public class ImageHeapLayoutInfo {
@UnknownPrimitiveField(availability = AfterHeapLayout.class) private final long writablePatchedOffset;
@UnknownPrimitiveField(availability = AfterHeapLayout.class) private final long writablePatchedSize;
- public ImageHeapLayoutInfo(long startOffset, long imageHeapSize, long writableOffset, long writableSize, long readOnlyRelocatableOffset, long readOnlyRelocatableSize, long writablePatchedOffset,
+ @SuppressWarnings("this-escape")
+ public ImageHeapLayoutInfo(long startOffset, long endOffset, long writableOffset, long writableSize, long readOnlyRelocatableOffset, long readOnlyRelocatableSize, long writablePatchedOffset,
long writablePatchedSize) {
this.startOffset = startOffset;
- this.imageHeapSize = imageHeapSize;
+ this.endOffset = endOffset;
this.writableOffset = writableOffset;
this.writableSize = writableSize;
this.readOnlyRelocatableOffset = readOnlyRelocatableOffset;
@@ -54,16 +57,34 @@ public ImageHeapLayoutInfo(long startOffset, long imageHeapSize, long writableOf
this.writablePatchedOffset = writablePatchedOffset;
this.writablePatchedSize = writablePatchedSize;
+ assert verifyAlignment();
assert readOnlyRelocatableOffset + readOnlyRelocatableSize <= writablePatchedOffset : Assertions.errorMessage("the writable patched section is placed after the relocations",
readOnlyRelocatableOffset, readOnlyRelocatableSize, writablePatchedOffset);
}
+ protected boolean verifyAlignment() {
+ assert startOffset % Heap.getHeap().getImageHeapAlignment() == 0;
+ assert endOffset % SubstrateOptions.getPageSize() == 0;
+ assert endOffset >= startOffset;
+ return true;
+ }
+
+ /**
+ * Returns the image heap start offset. This value is a multiple of
+ * {@link Heap#getImageHeapAlignment}.
+ */
public long getStartOffset() {
return startOffset;
}
- public long getImageHeapSize() {
- return imageHeapSize;
+ /** Returns the image heap end offset. This value is a multiple of the build-time page size. */
+ public long getEndOffset() {
+ return endOffset;
+ }
+
+ /** Returns the image heap size. This value is a multiple of the build-time page size. */
+ public long getSize() {
+ return endOffset - startOffset;
}
public long getWritableOffset() {
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/DynamicImageLayerInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/DynamicImageLayerInfo.java
index 2116735e9e76..c4ba1828345c 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/DynamicImageLayerInfo.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/DynamicImageLayerInfo.java
@@ -69,4 +69,8 @@ public static int getCurrentLayerNumber() {
return singleton().layerNumber;
}
}
+
+ public abstract int getPreviousMaxTypeId();
+
+ public abstract long getPreviousImageHeapEndOffset();
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metaspace/Metaspace.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metaspace/Metaspace.java
index 8b626395f342..a7430a48e3af 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metaspace/Metaspace.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metaspace/Metaspace.java
@@ -30,6 +30,7 @@
import org.graalvm.word.Pointer;
import com.oracle.svm.core.Uninterruptible;
+import com.oracle.svm.core.heap.ObjectVisitor;
import com.oracle.svm.core.hub.DynamicHub;
import jdk.graal.compiler.api.replacements.Fold;
@@ -46,6 +47,11 @@ static Metaspace singleton() {
return ImageSingletons.lookup(Metaspace.class);
}
+ @Fold
+ static boolean isSupported() {
+ return !(singleton() instanceof NoMetaspace);
+ }
+
/**
* Returns {@code true} if the {@link Object} reference points to a location within the address
* range of the metaspace. Usually faster than {@link #isInAllocatedMemory}.
@@ -82,4 +88,7 @@ static Metaspace singleton() {
/** Allocates a byte array. */
byte[] allocateByteArray(int length);
+
+ /** Walks all metaspace objects. May only be called at a safepoint. */
+ void walkObjects(ObjectVisitor visitor);
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metaspace/NoMetaspace.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metaspace/NoMetaspace.java
index e854b11b4116..1bb518ca0556 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metaspace/NoMetaspace.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metaspace/NoMetaspace.java
@@ -29,6 +29,7 @@
import org.graalvm.word.Pointer;
import com.oracle.svm.core.Uninterruptible;
+import com.oracle.svm.core.heap.ObjectVisitor;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.util.VMError;
@@ -66,4 +67,9 @@ public DynamicHub allocateDynamicHub(int numVTableEntries) {
public byte[] allocateByteArray(int length) {
throw VMError.shouldNotReachHere("Must not be called if metaspace support is not available.");
}
+
+ @Override
+ public void walkObjects(ObjectVisitor visitor) {
+ /* Nothing to do. */
+ }
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NativeMemoryTracking.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NativeMemoryTracking.java
index 2158e99a0090..c09d08dc6f49 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NativeMemoryTracking.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NativeMemoryTracking.java
@@ -28,7 +28,6 @@
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
-import jdk.graal.compiler.word.Word;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
@@ -39,12 +38,13 @@
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.VMInspectionOptions;
-import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.jdk.RuntimeSupport;
import com.oracle.svm.core.memory.NativeMemory;
+import com.oracle.svm.core.os.ImageHeapProvider;
import com.oracle.svm.core.util.UnsignedUtils;
import jdk.graal.compiler.api.replacements.Fold;
+import jdk.graal.compiler.word.Word;
/**
* This class implements native memory tracking (NMT). There are two components to NMT: tracking
@@ -313,9 +313,8 @@ private NmtVirtualMemoryInfo getVirtualInfo(int category) {
public static RuntimeSupport.Hook initializationHook() {
return isFirstIsolate -> {
- // Track the image heap virtual memory usage.
- NativeMemoryTracking.singleton().trackReserve(Heap.getHeap().getImageHeapReservedBytes(), NmtCategory.ImageHeap);
- NativeMemoryTracking.singleton().trackCommit(Heap.getHeap().getImageHeapCommittedBytes(), NmtCategory.ImageHeap);
+ NativeMemoryTracking.singleton().trackReserve(ImageHeapProvider.get().getImageHeapReservedBytes(), NmtCategory.ImageHeap);
+ NativeMemoryTracking.singleton().trackCommit(ImageHeapProvider.get().getImageHeapMappedBytes(), NmtCategory.ImageHeap);
};
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtCategory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtCategory.java
index e0830ea80d83..24275e697b4c 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtCategory.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtCategory.java
@@ -29,6 +29,8 @@
/** Categories for native memory tracking. */
public enum NmtCategory {
+ /** Auxiliary images. */
+ AuxiliaryImage("Auxiliary Image"),
/** JIT compiler. */
Compiler("Compiler"),
/** JIT compiled code. */
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCommittedMemoryProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCommittedMemoryProvider.java
index e244b2fb7dc8..2e7f8b7ce368 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCommittedMemoryProvider.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCommittedMemoryProvider.java
@@ -125,18 +125,10 @@ protected static void free(PointerBase start, UnsignedWord nbytes, NmtCategory n
VMError.guarantee(result == 0, "Error while freeing virtual memory.");
}
- @Override
- public UnsignedWord getCollectedHeapAddressSpaceSize() {
- /* Only a part of the address space is available for the collected Java heap. */
- UnsignedWord reservedForJavaHeap = getReservedAddressSpaceSize().subtract(getReservedMetaspaceSize());
- UnsignedWord imageHeapSize = Heap.getHeap().getImageHeapReservedBytes();
- assert reservedForJavaHeap.aboveThan(imageHeapSize);
- return reservedForJavaHeap.subtract(imageHeapSize);
- }
-
- /** The total number of bytes reserved for the whole address space. */
+ /**
+ * The total number of bytes reserved for the whole address space. This address space contains
+ * at least the image heap and the collected Java heap, but may also contain other data such as
+ * the null regions, the metaspace, or auxiliary images.
+ */
protected abstract UnsignedWord getReservedAddressSpaceSize();
-
- /** The number of address space bytes that are reserved for the metaspace. */
- protected abstract UnsignedWord getReservedMetaspaceSize();
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCopyingImageHeapProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCopyingImageHeapProvider.java
index f81660685d81..073db412d2d7 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCopyingImageHeapProvider.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCopyingImageHeapProvider.java
@@ -25,12 +25,9 @@
package com.oracle.svm.core.os;
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_BEGIN;
-import static com.oracle.svm.core.Isolates.IMAGE_HEAP_END;
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_WRITABLE_BEGIN;
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_WRITABLE_END;
-import static com.oracle.svm.core.util.PointerUtils.roundUp;
-import jdk.graal.compiler.word.Word;
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.word.Pointer;
import org.graalvm.word.PointerBase;
@@ -41,16 +38,19 @@
import com.oracle.svm.core.code.DynamicMethodAddressResolutionHeapSupport;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.os.VirtualMemoryProvider.Access;
+import com.oracle.svm.core.util.PointerUtils;
import com.oracle.svm.core.util.UnsignedUtils;
+import jdk.graal.compiler.word.Word;
+
public abstract class AbstractCopyingImageHeapProvider extends AbstractImageHeapProvider {
@Override
@Uninterruptible(reason = "Called during isolate initialization.")
- public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, WordPointer basePointer, WordPointer endPointer) {
+ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, WordPointer heapBaseOut, WordPointer imageHeapEndOut) {
Pointer selfReservedMemory = Word.nullPointer();
UnsignedWord requiredSize = getTotalRequiredAddressSpaceSize();
if (reservedAddressSpace.isNull()) {
- UnsignedWord alignment = Word.unsigned(Heap.getHeap().getPreferredAddressSpaceAlignment());
+ UnsignedWord alignment = Word.unsigned(Heap.getHeap().getHeapBaseAlignment());
selfReservedMemory = VirtualMemoryProvider.get().reserve(requiredSize, alignment, false);
if (selfReservedMemory.isNull()) {
return CEntryPointErrors.RESERVE_ADDRESS_SPACE_FAILED;
@@ -88,7 +88,7 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W
}
// Copy the memory to the reserved address space.
- UnsignedWord imageHeapSizeInFile = getImageHeapSizeInFile(IMAGE_HEAP_BEGIN.get(), IMAGE_HEAP_END.get());
+ UnsignedWord imageHeapSizeInFile = getImageHeapSizeInFile();
Pointer imageHeap = getImageHeapBegin(heapBase);
int result = commitAndCopyMemory(IMAGE_HEAP_BEGIN.get(), imageHeapSizeInFile, imageHeap);
if (result != CEntryPointErrors.NO_ERROR) {
@@ -117,11 +117,14 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W
}
}
- // Update the heap base and end pointers.
- basePointer.write(heapBase);
- if (endPointer.isNonNull()) {
- endPointer.write(roundUp(imageHeap.add(imageHeapSizeInFile), pageSize));
- }
+ /* Update heap base and image heap end. */
+ assert PointerUtils.isAMultiple(heapBase, Word.unsigned(Heap.getHeap().getHeapBaseAlignment()));
+ heapBaseOut.write(heapBase);
+
+ Pointer imageHeapEnd = getImageHeapEnd(heapBase);
+ assert PointerUtils.isAMultiple(imageHeapEnd, pageSize);
+ imageHeapEndOut.write(imageHeapEnd);
+
return CEntryPointErrors.NO_ERROR;
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractImageHeapProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractImageHeapProvider.java
index a9aa47620350..692d74584a65 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractImageHeapProvider.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractImageHeapProvider.java
@@ -26,62 +26,157 @@
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_BEGIN;
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_END;
+import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
+import static com.oracle.svm.core.imagelayer.ImageLayerSection.SectionEntries.HEAP_BEGIN;
+import static com.oracle.svm.core.imagelayer.ImageLayerSection.SectionEntries.HEAP_END;
+import static com.oracle.svm.core.imagelayer.ImageLayerSection.SectionEntries.NEXT_SECTION;
import static com.oracle.svm.core.util.PointerUtils.roundUp;
+import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.word.Pointer;
import org.graalvm.word.PointerBase;
import org.graalvm.word.UnsignedWord;
+import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.Uninterruptible;
+import com.oracle.svm.core.c.CGlobalData;
+import com.oracle.svm.core.c.CGlobalDataFactory;
import com.oracle.svm.core.code.DynamicMethodAddressResolutionHeapSupport;
import com.oracle.svm.core.heap.Heap;
+import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
+import com.oracle.svm.core.imagelayer.ImageLayerSection;
import com.oracle.svm.core.util.UnsignedUtils;
+import com.oracle.svm.core.util.VMError;
import jdk.graal.compiler.word.Word;
public abstract class AbstractImageHeapProvider implements ImageHeapProvider {
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ private static final CGlobalData CACHED_RESERVED_IMAGE_HEAP_SIZE = CGlobalDataFactory.createWord();
+ private static final CGlobalData CACHED_COMMITTED_IMAGE_HEAP_SIZE = CGlobalDataFactory.createWord();
+
+ /**
+ * The number of address space bytes that are needed for all data up to the end of the image
+ * heap (excluding any auxiliary images). This value is a multiple of the build-time page size.
+ */
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
protected UnsignedWord getTotalRequiredAddressSpaceSize() {
- UnsignedWord size = getImageHeapAddressSpaceSize();
+ UnsignedWord size = getImageHeapEndOffsetInAddressSpace();
if (DynamicMethodAddressResolutionHeapSupport.isEnabled()) {
size = size.add(getPreHeapAlignedSizeForDynamicMethodAddressResolver());
}
+ assert UnsignedUtils.isAMultiple(size, Word.unsigned(SubstrateOptions.getPageSize()));
return size;
}
@Override
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
- public UnsignedWord getImageHeapAddressSpaceSize() {
- UnsignedWord pageSize = VirtualMemoryProvider.get().getGranularity();
- int imageHeapOffset = Heap.getHeap().getImageHeapOffsetInAddressSpace();
- assert imageHeapOffset >= 0;
- UnsignedWord size = Word.unsigned(imageHeapOffset);
- size = size.add(getImageHeapSizeInFile());
- size = UnsignedUtils.roundUp(size, pageSize);
- return size;
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ public UnsignedWord getImageHeapEndOffsetInAddressSpace() {
+ UnsignedWord imageHeapOffset = Word.unsigned(Heap.getHeap().getImageHeapOffsetInAddressSpace());
+ UnsignedWord result = imageHeapOffset.add(getImageHeapAddressSpaceSize());
+ assert UnsignedUtils.isAMultiple(result, Word.unsigned(SubstrateOptions.getPageSize()));
+ return result;
}
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ @Override
+ public UnsignedWord getImageHeapReservedBytes() {
+ /*
+ * This assumes that all image heaps are mapped directly after each other, with no other
+ * data in between (except for gaps that are needed for alignment reasons).
+ */
+ UnsignedWord result = getImageHeapEndOffsetInAddressSpace().subtract(Heap.getHeap().getImageHeapOffsetInAddressSpace());
+ assert UnsignedUtils.isAMultiple(result, Word.unsigned(SubstrateOptions.getPageSize()));
+ return result;
+ }
+
+ @Override
+ public UnsignedWord getImageHeapMappedBytes() {
+ if (!ImageLayerBuildingSupport.buildingImageLayer()) {
+ return getImageHeapSizeInFile();
+ }
+
+ /* Check if value is cached. */
+ Word currentValue = CACHED_COMMITTED_IMAGE_HEAP_SIZE.get().read();
+ if (currentValue.notEqual(Word.zero())) {
+ return currentValue;
+ }
+
+ /* Walk through the sections and add up the layer image heap sizes. */
+ UnsignedWord result = Word.zero();
+ Pointer currentSection = ImageLayerSection.getInitialLayerSection().get();
+ while (currentSection.isNonNull()) {
+ Word heapBegin = currentSection.readWord(ImageLayerSection.getEntryOffset(HEAP_BEGIN));
+ Word heapEnd = currentSection.readWord(ImageLayerSection.getEntryOffset(HEAP_END));
+ UnsignedWord size = getImageHeapSizeInFile(heapBegin, heapEnd);
+ result = result.add(size);
+
+ currentSection = currentSection.readWord(ImageLayerSection.getEntryOffset(NEXT_SECTION));
+ }
+ CACHED_COMMITTED_IMAGE_HEAP_SIZE.get().write(result);
+ assert UnsignedUtils.isAMultiple(result, Word.unsigned(SubstrateOptions.getPageSize()));
+ return result;
+ }
+
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
protected static UnsignedWord getImageHeapSizeInFile(Word beginAddress, Word endAddress) {
assert endAddress.aboveOrEqual(endAddress);
- return endAddress.subtract(beginAddress);
+ UnsignedWord result = endAddress.subtract(beginAddress);
+ assert UnsignedUtils.isAMultiple(result, Word.unsigned(SubstrateOptions.getPageSize()));
+ return result;
}
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
public static UnsignedWord getImageHeapSizeInFile() {
- return getImageHeapSizeInFile(IMAGE_HEAP_BEGIN.get(), IMAGE_HEAP_END.get());
+ VMError.guarantee(!ImageLayerBuildingSupport.buildingImageLayer());
+ UnsignedWord result = getImageHeapSizeInFile(IMAGE_HEAP_BEGIN.get(), IMAGE_HEAP_END.get());
+ assert UnsignedUtils.isAMultiple(result, Word.unsigned(SubstrateOptions.getPageSize()));
+ return result;
}
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
protected static Pointer getImageHeapBegin(Pointer heapBase) {
return heapBase.add(Heap.getHeap().getImageHeapOffsetInAddressSpace());
}
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ protected Pointer getImageHeapEnd(Pointer heapBase) {
+ return heapBase.add(getImageHeapEndOffsetInAddressSpace());
+ }
+
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
protected static UnsignedWord getPreHeapAlignedSizeForDynamicMethodAddressResolver() {
UnsignedWord requiredPreHeapMemoryInBytes = DynamicMethodAddressResolutionHeapSupport.get().getRequiredPreHeapMemoryInBytes();
- /* Ensure there is enough space to properly align the heap */
- UnsignedWord heapAlignment = Word.unsigned(Heap.getHeap().getPreferredAddressSpaceAlignment());
- return roundUp((PointerBase) requiredPreHeapMemoryInBytes, heapAlignment);
+ /* Ensure there is enough space to properly align the heap base. */
+ UnsignedWord heapBaseAlignment = Word.unsigned(Heap.getHeap().getHeapBaseAlignment());
+ return roundUp((PointerBase) requiredPreHeapMemoryInBytes, heapBaseAlignment);
+ }
+
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ private static UnsignedWord getImageHeapAddressSpaceSize() {
+ if (!ImageLayerBuildingSupport.buildingImageLayer()) {
+ return getImageHeapSizeInFile();
+ }
+
+ /* Check if value is cached. */
+ Word currentValue = CACHED_RESERVED_IMAGE_HEAP_SIZE.get().read();
+ if (currentValue.notEqual(Word.zero())) {
+ return currentValue;
+ }
+
+ /* Walk through the sections, align the start of each image heap, and add up the sizes. */
+ UnsignedWord result = Word.zero();
+ Pointer currentSection = ImageLayerSection.getInitialLayerSection().get();
+ while (currentSection.isNonNull()) {
+ result = UnsignedUtils.roundUp(result, Word.unsigned(Heap.getHeap().getImageHeapAlignment()));
+
+ Word heapBegin = currentSection.readWord(ImageLayerSection.getEntryOffset(HEAP_BEGIN));
+ Word heapEnd = currentSection.readWord(ImageLayerSection.getEntryOffset(HEAP_END));
+ UnsignedWord size = getImageHeapSizeInFile(heapBegin, heapEnd);
+ result = result.add(size);
+
+ currentSection = currentSection.readWord(ImageLayerSection.getEntryOffset(NEXT_SECTION));
+ }
+ CACHED_RESERVED_IMAGE_HEAP_SIZE.get().write(result);
+ assert UnsignedUtils.isAMultiple(result, Word.unsigned(SubstrateOptions.getPageSize()));
+ return result;
}
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java
index 4f974343496d..f77034f534e1 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CommittedMemoryProvider.java
@@ -74,8 +74,8 @@ default UnsignedWord getGranularity() {
/**
* Returns the size of the address space that is reserved for the collected Java heap (i.e.,
- * this explicitly excludes all heap parts that are not collected, such as the image heap or the
- * protected memory before the image heap).
+ * this explicitly excludes all other data, such as null regions, metaspace, image heap, or
+ * auxiliary images).
*/
UnsignedWord getCollectedHeapAddressSpaceSize();
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CopyingImageHeapProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CopyingImageHeapProvider.java
index 966f9a63c0f0..bb30a75be8b1 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CopyingImageHeapProvider.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/CopyingImageHeapProvider.java
@@ -24,12 +24,18 @@
*/
package com.oracle.svm.core.os;
+import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
+
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;
-import com.oracle.svm.core.UnmanagedMemoryUtil;
import com.oracle.svm.core.Uninterruptible;
+import com.oracle.svm.core.UnmanagedMemoryUtil;
import com.oracle.svm.core.c.function.CEntryPointErrors;
+import com.oracle.svm.core.heap.Heap;
+import com.oracle.svm.core.util.UnsignedUtils;
+
+import jdk.graal.compiler.word.Word;
/** Platform independent image heap provider that deep-copies the image heap memory. */
public class CopyingImageHeapProvider extends AbstractCopyingImageHeapProvider {
@@ -39,4 +45,19 @@ protected int copyMemory(Pointer loadedImageHeap, UnsignedWord imageHeapSize, Po
UnmanagedMemoryUtil.copy(loadedImageHeap, newImageHeap, imageHeapSize);
return CEntryPointErrors.NO_ERROR;
}
+
+ /**
+ * Only called from legacy code, see GR-68584. This method does something fundamentally
+ * different than the method with the same name in {@link AbstractImageHeapProvider}.
+ */
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ protected UnsignedWord getImageHeapAddressSpaceSize() {
+ UnsignedWord pageSize = VirtualMemoryProvider.get().getGranularity();
+ int imageHeapOffset = Heap.getHeap().getImageHeapOffsetInAddressSpace();
+ assert imageHeapOffset >= 0;
+ UnsignedWord size = Word.unsigned(imageHeapOffset);
+ size = size.add(getImageHeapSizeInFile());
+ size = UnsignedUtils.roundUp(size, pageSize);
+ return size;
+ }
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/ImageHeapProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/ImageHeapProvider.java
index 3f2dd1328caa..efd2e342b3f4 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/ImageHeapProvider.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/ImageHeapProvider.java
@@ -35,7 +35,7 @@
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.c.function.CEntryPointErrors;
-import com.oracle.svm.core.heap.Heap;
+import com.oracle.svm.core.image.ImageHeapLayouter;
import jdk.graal.compiler.api.replacements.Fold;
@@ -48,25 +48,28 @@
* that this mode is deprecated and not covered by the documentation below.
*
* If {@link SubstrateOptions#SpawnIsolates} is enabled, a heap base is used and the image heap is
- * explicitly mapped into a contiguous address space. Here is the typical memory layout of a mapped
- * image heap at run-time:
+ * explicitly mapped into a contiguous address space. Here is the typical memory layout of the
+ * address space at run-time:
*
*
- * |---------------------------------------------------------------------------------------|
- * | protected memory | read-only | writable | read-only (optional) |
- * |---------------------------------------------------------------------------------------|
- * | | normal objects | relocatable data | normal objects |
- * |---------------------------------------------------------------------------------------|
- * ^
- * heapBase
+ * |----------------------------------------------------------------------------------|
+ * | protected memory | metaspace | read-only | writable | read-only |
+ * | null regions | (optional) | | | (optional) |
+ * |----------------------------------------------------------------------------------|
+ * | | normal data | relocatable data | normal data |
+ * |----------------------------------------------------------------------------------|
+ * ^ ^ ^
+ * heapBase image heap begin image heap end
*
*
- * The memory at the heap base is explicitly marked as inaccessible (see
- * {@link Heap#getImageHeapOffsetInAddressSpace()} for more details). Accesses to it will result in
- * a segfault.
+ * The memory at the heap base is explicitly marked as inaccessible. Accesses to it will result in a
+ * segfault.
*
* Note that the relocatable data may overlap with both the read-only and writable part of the image
* heap. Besides that, parts of the read-only relocatable data may be writable at run-time.
+ *
+ * For more information on the structure of the image heap, see the classes that implement
+ * {@link ImageHeapLayouter}.
*/
public interface ImageHeapProvider {
@Fold
@@ -83,14 +86,14 @@ static ImageHeapProvider get() {
* image heap at an arbitrary address.
* @param reservedSize If {@code reservedAddressSpace} is non-null, the number of reserved bytes
* at that address.
- * @param basePointer An address where a pointer to the start address of the image heap instance
- * will be written. Must not be null.
- * @param endPointer An address where a pointer to the end of the image heap instance will be
- * written. May be null if this value is not required.
+ * @param heapBaseOut An address where a pointer to the heap base will be written to. Must not
+ * be null.
+ * @param imageHeapEndOut An address where a pointer to the end of the image heap will be
+ * written to. Must not be null.
* @return a result code from {@link CEntryPointErrors}.
*/
@Uninterruptible(reason = "Still being initialized.")
- int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, WordPointer basePointer, WordPointer endPointer);
+ int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, WordPointer heapBaseOut, WordPointer imageHeapEndOut);
/**
* Disposes an instance of the image heap that was created with this provider. This method must
@@ -99,6 +102,22 @@ static ImageHeapProvider get() {
@Uninterruptible(reason = "Called during isolate tear-down.")
int freeImageHeap(PointerBase heapBase);
+ /**
+ * Returns the end offset of the image heap, relative to the heap base (excluding any auxiliary
+ * images). This value is a multiple of the build-time page size.
+ */
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
- UnsignedWord getImageHeapAddressSpaceSize();
+ UnsignedWord getImageHeapEndOffsetInAddressSpace();
+
+ /**
+ * Number of reserved image heap bytes (excluding any auxiliary images). This value is a
+ * multiple of the build-time page size.
+ */
+ UnsignedWord getImageHeapReservedBytes();
+
+ /**
+ * Number of mapped image heap bytes (excluding any auxiliary images). This value is a multiple
+ * of the build-time page size.
+ */
+ UnsignedWord getImageHeapMappedBytes();
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/OSCommittedMemoryProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/OSCommittedMemoryProvider.java
index 843426a5291f..441db246a857 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/OSCommittedMemoryProvider.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/OSCommittedMemoryProvider.java
@@ -29,6 +29,7 @@
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
+import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.word.UnsignedWord;
@@ -51,15 +52,16 @@ public OSCommittedMemoryProvider() {
@Override
@Uninterruptible(reason = "Still being initialized.")
- public int initialize(WordPointer heapBasePointer, IsolateArguments arguments) {
+ public int initialize(WordPointer heapBaseOut, IsolateArguments arguments) {
if (!SubstrateOptions.SpawnIsolates.getValue()) {
int result = protectSingleIsolateImageHeap();
if (result == CEntryPointErrors.NO_ERROR) {
- heapBasePointer.write(Isolates.IMAGE_HEAP_BEGIN.get());
+ heapBaseOut.write(Isolates.IMAGE_HEAP_BEGIN.get());
}
return result;
}
- return ImageHeapProvider.get().initialize(nullPointer(), zero(), heapBasePointer, nullPointer());
+ WordPointer imageHeapEndOut = StackValue.get(WordPointer.class);
+ return ImageHeapProvider.get().initialize(nullPointer(), zero(), heapBaseOut, imageHeapEndOut);
}
@Override
@@ -71,6 +73,12 @@ public int tearDown() {
return ImageHeapProvider.get().freeImageHeap(KnownIntrinsics.heapBase());
}
+ @Override
+ public UnsignedWord getCollectedHeapAddressSpaceSize() {
+ assert getReservedAddressSpaceSize().aboveOrEqual(ImageHeapProvider.get().getImageHeapEndOffsetInAddressSpace());
+ return getReservedAddressSpaceSize().subtract(ImageHeapProvider.get().getImageHeapEndOffsetInAddressSpace());
+ }
+
@Override
public UnsignedWord getReservedAddressSpaceSize() {
UnsignedWord maxAddressSpaceSize = ReferenceAccess.singleton().getMaxAddressSpaceSize();
@@ -80,9 +88,4 @@ public UnsignedWord getReservedAddressSpaceSize() {
}
return maxAddressSpaceSize;
}
-
- @Override
- protected UnsignedWord getReservedMetaspaceSize() {
- return Word.zero();
- }
}
diff --git a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp
index 00397f018d8a..a92a65567228 100644
--- a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp
+++ b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp
@@ -285,7 +285,7 @@ struct SharedLayerSnapshot {
nextConstantId @3 :ConstantId;
staticPrimitiveFieldsConstantId @4 :ConstantId;
staticObjectFieldsConstantId @5 :ConstantId;
- imageHeapSize @6 :Int64;
+ imageHeapEndOffset @6 :Int64;
constantsToRelink @7 :List(ConstantId);
types @8 :List(PersistedAnalysisType);
methods @9 :List(PersistedAnalysisMethod);
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java
index f6ebcb659997..80618a3cf8a1 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java
@@ -33,7 +33,6 @@
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.hub.ClassForNameSupport;
-import com.oracle.svm.core.hub.DynamicHubSupport;
import com.oracle.svm.core.hub.RuntimeClassLoading;
import com.oracle.svm.core.hub.registry.ClassRegistries;
@@ -69,9 +68,4 @@ private static void onTypeReachable(Class> cls) {
ClassRegistries.addAOTClass(ClassLoaderFeature.getRuntimeClassLoader(cls.getClassLoader()), cls);
}
}
-
- @Override
- public void afterCompilation(AfterCompilationAccess access) {
- ClassRegistries.setStartingTypeId(DynamicHubSupport.currentLayer().getMaxTypeId() + 1);
- }
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
index c2e532b4a054..dc3248aa5e64 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
@@ -154,7 +154,6 @@
import com.oracle.svm.core.graal.word.SubstrateWordOperationPlugins;
import com.oracle.svm.core.graal.word.SubstrateWordTypes;
import com.oracle.svm.core.heap.BarrierSetProvider;
-import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.heap.RestrictHeapAccessCallees;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.image.ImageHeapLayouter;
@@ -1106,10 +1105,6 @@ protected void setupNativeImage(OptionValues options, Map types) {
- int maxTypeId = types.stream().mapToInt(t -> t.getHub().getTypeID()).max().orElse(0);
- assert maxTypeId > 0;
-
UnsafeArrayTypeWriter output = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess());
- encodeMetadata(output, types, maxTypeId);
+ encodeMetadata(output, types);
int length = TypeConversion.asS4(output.getBytesWritten());
return output.toArray(new byte[length]);
}
- private static void encodeMetadata(UnsafeArrayTypeWriter output, Collection extends SharedType> types, int maxTypeId) {
+ private static void encodeMetadata(UnsafeArrayTypeWriter output, Collection extends SharedType> types) {
/* Write header. */
long totalFieldCountOffset = output.getBytesWritten();
output.putS4(0); // total field count (patched later on)
@@ -153,26 +150,16 @@ private static void encodeMetadata(UnsafeArrayTypeWriter output, Collection ex
long fieldNameCountOffset = output.getBytesWritten();
output.putS4(0); // field name count (patched later on)
- output.putUV(maxTypeId);
-
- /*
- * Handle layered-related tasks:
- *
- * 1) Determine the lower bound of typeIDs written into this layer's encoded metadata. In a
- * layered build, the types with typeIDs below the lower bound have already been written in
- * a prior layer's encoded metadata.
- *
- * 2) Create map of field names and initialize the map with field names already installed in
- * prior layer's encoded metadata. It is legal for this layer's encoded metadata to
- * reference field map entries installed by prior layers. Only newly added field names will
- * be encoded within this layer's encoded metadata.
- */
EconomicMap fieldNames = EconomicMap.create();
int priorFieldNamesSize = 0;
- int typeIDLowerBound = 0;
+ int prevLayersMaxTypeId = -1;
if (ImageLayerBuildingSupport.buildingExtensionLayer()) {
LayeredHeapDumpEncodedTypesTracker layeredTracking = ImageSingletons.lookup(LayeredHeapDumpEncodedTypesTracker.class);
- typeIDLowerBound = layeredTracking.getStartingTypeId();
+ prevLayersMaxTypeId = DynamicImageLayerInfo.singleton().getPreviousMaxTypeId();
+ /*
+ * We only need to encode newly added field names within this layer's metadata. So, we
+ * pre-populate the map with all the field names of previous layers.
+ */
for (String fieldName : layeredTracking.getPriorFieldNames()) {
fieldNames.put(fieldName, priorFieldNamesSize);
priorFieldNamesSize++;
@@ -186,10 +173,8 @@ private static void encodeMetadata(UnsafeArrayTypeWriter output, Collection ex
for (SharedType type : types) {
if (type.isInstanceClass()) {
int typeId = type.getHub().getTypeID();
- if (typeId < typeIDLowerBound) {
- /*
- * This type's information has been installed in a prior layer.
- */
+ if (typeId <= prevLayersMaxTypeId) {
+ /* This type's information has been installed in a prior layer. */
continue;
}
ArrayList instanceFields = collectFields(type.getInstanceFields(false));
@@ -300,23 +285,17 @@ private static HProfType getType(SharedField field) {
@AutomaticallyRegisteredImageSingleton(onlyWith = BuildingImageLayerPredicate.class)
class LayeredHeapDumpEncodedTypesTracker implements LayeredImageSingleton {
- private final int startingTypeId;
private List encodedFieldNames;
private final List priorFieldNames;
LayeredHeapDumpEncodedTypesTracker() {
- this(0, List.of());
+ this(List.of());
}
- LayeredHeapDumpEncodedTypesTracker(int startingTypeId, List priorFieldNames) {
- this.startingTypeId = startingTypeId;
+ LayeredHeapDumpEncodedTypesTracker(List priorFieldNames) {
this.priorFieldNames = priorFieldNames;
}
- public int getStartingTypeId() {
- return startingTypeId;
- }
-
void recordEncodedFieldNames(List nameList) {
this.encodedFieldNames = nameList;
}
@@ -332,7 +311,6 @@ public EnumSet getImageBuilderFlags() {
@Override
public PersistFlags preparePersist(ImageSingletonWriter writer) {
- writer.writeInt("startingTypeId", DynamicHubSupport.currentLayer().getMaxTypeId());
writer.writeStringList("encodedFieldNames", encodedFieldNames);
return PersistFlags.CREATE;
}
@@ -340,6 +318,6 @@ public PersistFlags preparePersist(ImageSingletonWriter writer) {
@SuppressWarnings("unused")
public static Object createFromLoader(ImageSingletonLoader loader) {
List encodedFieldNames = Collections.unmodifiableList(loader.readStringList("encodedFieldNames"));
- return new LayeredHeapDumpEncodedTypesTracker(loader.readInt("startingTypeId"), encodedFieldNames);
+ return new LayeredHeapDumpEncodedTypesTracker(encodedFieldNames);
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java
index 9eb8edcc081c..03d5a606a891 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java
@@ -24,7 +24,6 @@
*/
package com.oracle.svm.hosted.image;
-import static com.oracle.svm.core.SubstrateOptions.MremapImageHeap;
import static com.oracle.svm.core.SubstrateOptions.SpawnIsolates;
import static com.oracle.svm.core.SubstrateUtil.mangleName;
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
@@ -50,7 +49,6 @@
import java.util.Set;
import java.util.stream.Collectors;
-import com.oracle.svm.hosted.ByteFormattingUtil;
import org.graalvm.collections.Pair;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.c.CHeader;
@@ -107,6 +105,7 @@
import com.oracle.svm.core.reflect.SubstrateAccessor;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
+import com.oracle.svm.hosted.ByteFormattingUtil;
import com.oracle.svm.hosted.DeadlockWatchdog;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.NativeImageOptions;
@@ -445,12 +444,10 @@ public void build(String imageName, DebugContext debug) {
int pageSize = objectFile.getPageSize();
- long imageHeapSize = getImageHeapSize();
-
if (ImageLayerBuildingSupport.buildingImageLayer()) {
ImageSingletons.lookup(ImageLayerSectionFeature.class).createSection(objectFile, heapLayout);
if (ImageLayerBuildingSupport.buildingSharedLayer()) {
- HostedImageLayerBuildingSupport.singleton().getWriter().setImageHeapSize(imageHeapSize);
+ HostedImageLayerBuildingSupport.singleton().getWriter().setEndOffset(heapLayout.getEndOffset());
}
}
@@ -493,26 +490,16 @@ public void build(String imageName, DebugContext debug) {
(offset, symbolName, isGlobalSymbol) -> defineRelocationForSymbol(symbolName, offset));
// - Write the heap to its own section.
- // Dynamic linkers/loaders generally don't ensure any alignment to more than page
- // boundaries, so we take care of this ourselves in CommittedMemoryProvider, if we can.
- int alignment = pageSize;
-
- /*
- * Manually add padding to the SVM_HEAP section, because when SpawnIsolates are disabled
- * we operate with mprotect on it with page size granularity. Similarly, using mremap
- * aligns up the page boundary and may reset memory outside of the image heap.
- */
- boolean padImageHeap = !SpawnIsolates.getValue() || MremapImageHeap.getValue();
- long paddedImageHeapSize = padImageHeap ? NumUtil.roundUp(imageHeapSize, alignment) : imageHeapSize;
+ long imageHeapSize = getImageHeapSize();
+ RelocatableBuffer heapSectionBuffer = new RelocatableBuffer(imageHeapSize, objectFile.getByteOrder());
- VMError.guarantee(NumUtil.isInt(paddedImageHeapSize),
+ VMError.guarantee(NumUtil.isInt(imageHeapSize),
"The size of the image heap is %s and therefore too large. It must be smaller than %s. This can happen when very large resource files are included in the image or a build time initialized class creates a large cache.",
- ByteFormattingUtil.bytesToHuman(paddedImageHeapSize),
+ ByteFormattingUtil.bytesToHuman(imageHeapSize),
ByteFormattingUtil.bytesToHuman(Integer.MAX_VALUE));
- RelocatableBuffer heapSectionBuffer = new RelocatableBuffer(paddedImageHeapSize, objectFile.getByteOrder());
ProgbitsSectionImpl heapSectionImpl = new BasicProgbitsSectionImpl(heapSectionBuffer.getBackingArray());
// Note: On isolate startup the read only part of the heap will be set up as such.
- heapSection = objectFile.newProgbitsSection(SectionName.SVM_HEAP.getFormatDependentName(objectFile.getFormat()), alignment, true, false, heapSectionImpl);
+ heapSection = objectFile.newProgbitsSection(SectionName.SVM_HEAP.getFormatDependentName(objectFile.getFormat()), pageSize, true, false, heapSectionImpl);
objectFile.createDefinedSymbol(heapSection.getName(), heapSection, 0, 0, false, false);
long sectionOffsetOfARelocatablePointer = writer.writeHeap(debug, heapSectionBuffer);
@@ -886,7 +873,7 @@ public static String globalSymbolNameForMethod(ResolvedJavaMethod sm) {
@Override
public long getImageHeapSize() {
- return heapLayout.getImageHeapSize();
+ return heapLayout.getSize();
}
@Override
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java
index 8bd1441c6a32..2da4d9e8b1cd 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java
@@ -245,7 +245,7 @@ public void beforeImageWrite(BeforeImageWriteAccess access) {
private void generateRelocationPatchArray() {
int shift = ImageSingletons.lookup(CompressEncoding.class).getShift();
List patchArray = new ArrayList<>();
- int heapBeginOffset = tracker.getImageHeapBeginOffset();
+ int heapBeginOffset = Heap.getHeap().getImageHeapOffsetInAddressSpace();
assert heapBeginOffset >= 0 : "invalid image heap begin offset " + heapBeginOffset;
for (var entry : tracker.futureKeyToPatchingOffsetsMap.entrySet()) {
List offsetsToPatch = entry.getValue();
@@ -395,18 +395,10 @@ public int[] getRelocationPatches() {
}
class ImageLayerIdTrackingSingleton implements LayeredImageSingleton {
- private static final int UNKNOWN_HEAP_BEGIN_OFFSET = -1;
-
private final Map keyToTrackingInfoMap = new HashMap<>();
final Map> futureKeyToPatchingOffsetsMap = new ConcurrentHashMap<>();
- private final int imageHeapBeginOffset;
ImageLayerIdTrackingSingleton() {
- this(UNKNOWN_HEAP_BEGIN_OFFSET);
- }
-
- ImageLayerIdTrackingSingleton(int imageHeapBeginOffset) {
- this.imageHeapBeginOffset = imageHeapBeginOffset;
}
TrackingInfo getTrackingInfo(String key) {
@@ -419,10 +411,6 @@ void registerPriorTrackingInfo(String key, int constantId) {
VMError.guarantee(previous == null, "Two values are registered for this key %s", key);
}
- public int getImageHeapBeginOffset() {
- return imageHeapBeginOffset;
- }
-
public void registerFutureTrackingInfo(FutureTrackingInfo info) {
updateFutureTrackingInfo0(info, false);
}
@@ -500,13 +488,12 @@ public PersistFlags preparePersist(ImageSingletonWriter writer) {
writer.writeIntList("futureLoaderIds", futureLoaderIds);
writer.writeIntList("futureOffsets", futureOffsets);
- writer.writeInt("imageHeapBeginOffset", imageHeapBeginOffset == UNKNOWN_HEAP_BEGIN_OFFSET ? Heap.getHeap().getImageHeapOffsetInAddressSpace() : imageHeapBeginOffset);
return PersistFlags.CREATE;
}
@SuppressWarnings("unused")
public static Object createFromLoader(ImageSingletonLoader loader) {
- var tracker = new ImageLayerIdTrackingSingleton(loader.readInt("imageHeapBeginOffset"));
+ var tracker = new ImageLayerIdTrackingSingleton();
Iterator priorKeys = loader.readStringList("priorKeys").iterator();
Iterator priorIds = loader.readIntList("priorIds").iterator();
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java
index 4a3486f6c641..7f25ff6793b4 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java
@@ -271,4 +271,16 @@ public static Object createFromLoader(ImageSingletonLoader loader) {
return new HostedDynamicLayerInfo(layerNumber, codeSectionStartSymbol, libNames, previousLayerDelayedMethodSymbols, previousLayerDelayedMethodIds);
}
+
+ @Override
+ public int getPreviousMaxTypeId() {
+ SVMImageLayerLoader loader = HostedImageLayerBuildingSupport.singleton().getLoader();
+ return loader.getMaxTypeId();
+ }
+
+ @Override
+ public long getPreviousImageHeapEndOffset() {
+ SVMImageLayerLoader loader = HostedImageLayerBuildingSupport.singleton().getLoader();
+ return loader.getImageHeapEndOffset();
+ }
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java
index 0dda737921e0..47beeaccdabf 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java
@@ -1752,8 +1752,12 @@ public ImageHeapConstant getBaseLayerStaticObjectFields() {
return getOrCreateConstant(snapshot.getStaticObjectFieldsConstantId());
}
- public long getImageHeapSize() {
- return snapshot.getImageHeapSize();
+ public int getMaxTypeId() {
+ return snapshot.getNextTypeId() - 1;
+ }
+
+ public long getImageHeapEndOffset() {
+ return snapshot.getImageHeapEndOffset();
}
@Override
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java
index 0e5c080d1900..934ca301d089 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java
@@ -327,8 +327,8 @@ public void initializeExternalValues() {
imageLayerSnapshotUtil.initializeExternalValues();
}
- public void setImageHeapSize(long imageHeapSize) {
- snapshotBuilder.setImageHeapSize(imageHeapSize);
+ public void setEndOffset(long endOffset) {
+ snapshotBuilder.setImageHeapEndOffset(endOffset);
}
@Override
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java
index 896aa5f58aa1..505797f7bb92 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java
@@ -4883,10 +4883,10 @@ public final void setStaticObjectFieldsConstantId(int value) {
_setIntField(5, value);
}
- public final long getImageHeapSize() {
+ public final long getImageHeapEndOffset() {
return _getLongField(3);
}
- public final void setImageHeapSize(long value) {
+ public final void setImageHeapEndOffset(long value) {
_setLongField(3, value);
}
@@ -5114,7 +5114,7 @@ public final int getStaticObjectFieldsConstantId() {
return _getIntField(5);
}
- public final long getImageHeapSize() {
+ public final long getImageHeapEndOffset() {
return _getLongField(3);
}
diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/WebImageWasmCodeGen.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/WebImageWasmCodeGen.java
index 62b853454eb3..d7cca35917f7 100644
--- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/WebImageWasmCodeGen.java
+++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/WebImageWasmCodeGen.java
@@ -158,7 +158,7 @@ protected void setLayout(ImageHeapLayoutInfo layout) {
* {@link AbstractImage#getImageHeapSize()}).
*/
public long getImageHeapSize() {
- return getLayout().getImageHeapSize();
+ return getLayout().getSize();
}
/**
@@ -170,7 +170,7 @@ public long getImageHeapSize() {
* This number is mainly used to get the total heap size for image heap breakdown statistics.
*/
public long getFullImageHeapSize() {
- return getLayout().getImageHeapSize();
+ return getLayout().getSize();
}
@Override
diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/WebImageWasmLMCodeGen.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/WebImageWasmLMCodeGen.java
index cb741f427f0b..f1fbdd93bb0d 100644
--- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/WebImageWasmLMCodeGen.java
+++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/WebImageWasmLMCodeGen.java
@@ -98,12 +98,12 @@ protected void writeImageHeap() {
NativeImageHeapWriter writer = new NativeImageHeapWriter(codeCache.nativeImageHeap, layout);
- RelocatableBuffer heapSectionBuffer = new RelocatableBuffer(layout.getImageHeapSize(), WasmUtil.BYTE_ORDER);
+ RelocatableBuffer heapSectionBuffer = new RelocatableBuffer(layout.getSize(), WasmUtil.BYTE_ORDER);
codeCache.writeConstants(writer, heapSectionBuffer);
writer.writeHeap(debug, heapSectionBuffer);
long heapStart = MemoryLayout.HEAP_BASE.rawValue();
assert heapStart % 8 == 0 : heapStart;
- int imageHeapSize = (int) layout.getImageHeapSize();
+ int imageHeapSize = (int) layout.getSize();
EconomicMap, UnsignedWord> globalData = EconomicMap.create();
int memorySize = MemoryLayout.constructLayout(globalData, imageHeapSize, WebImageWasmOptions.StackSize.getValue());
diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/gc/WasmHeap.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/gc/WasmHeap.java
index f88d6fc6e5ff..dbec1c3f0c11 100644
--- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/gc/WasmHeap.java
+++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/gc/WasmHeap.java
@@ -175,7 +175,7 @@ public int getClassCount() {
}
@Override
- protected List> getAllClasses() {
+ protected List> getClassesInImageHeap() {
/* Two threads might race to set classList, but they compute the same result. */
if (classList == null) {
ArrayList> list = new ArrayList<>(imageHeapInfo.dynamicHubCount);
@@ -192,16 +192,6 @@ public UnsignedWord getUsedBytes() {
return Word.unsigned(WasmAllocation.getObjectSize());
}
- @Override
- public UnsignedWord getImageHeapReservedBytes() {
- throw VMError.shouldNotReachHere("Native Memory Tracking is not supported");
- }
-
- @Override
- public UnsignedWord getImageHeapCommittedBytes() {
- throw VMError.shouldNotReachHere("Native Memory Tracking is not supported");
- }
-
private static final class ClassListBuilderVisitor implements MemoryWalker.ImageHeapRegionVisitor, ObjectVisitor {
private final List> list;
@@ -253,8 +243,13 @@ public void endSafepoint() {
}
@Override
- public int getPreferredAddressSpaceAlignment() {
- throw VMError.shouldNotReachHere("WasmHeap.getPreferredAddressSpaceAlignment");
+ public int getHeapBaseAlignment() {
+ return 1;
+ }
+
+ @Override
+ public int getImageHeapAlignment() {
+ return 1;
}
@Override
diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/image/WasmGCHeapLayouter.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/image/WasmGCHeapLayouter.java
index 899864ff09e3..fe16f9cea54b 100644
--- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/image/WasmGCHeapLayouter.java
+++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/image/WasmGCHeapLayouter.java
@@ -56,8 +56,6 @@ public class WasmGCHeapLayouter implements ImageHeapLayouter {
*/
private final WasmGCPartition pseudoPartition = new WasmGCPartition("WasmGCPseudoPartition", true);
- private final long startOffset = 0;
-
@Override
public ImageHeapPartition[] getPartitions() {
return new ImageHeapPartition[]{singlePartition, pseudoPartition};
@@ -78,8 +76,8 @@ public WasmGCImageHeapLayoutInfo layout(ImageHeap imageHeap, int pageSize, Image
doLayout();
long totalSize = StreamSupport.stream(imageHeap.getObjects().spliterator(), false).mapToLong(ImageHeapObject::getSize).sum();
- long serializedSize = singlePartition.getStartOffset() + singlePartition.getSize() - startOffset;
- return new WasmGCImageHeapLayoutInfo(startOffset, serializedSize, totalSize);
+ long serializedSize = singlePartition.getStartOffset() + singlePartition.getSize();
+ return new WasmGCImageHeapLayoutInfo(serializedSize, totalSize);
}
private void doLayout() {
diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/image/WasmGCImageHeapLayoutInfo.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/image/WasmGCImageHeapLayoutInfo.java
index 47dfa924f39f..f4f9ac55079a 100644
--- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/image/WasmGCImageHeapLayoutInfo.java
+++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/image/WasmGCImageHeapLayoutInfo.java
@@ -47,12 +47,18 @@ public class WasmGCImageHeapLayoutInfo extends ImageHeapLayoutInfo {
* correspond directly to the image heap size in the Wasm binary (this cannot be
* known before the binary is assembled).
*/
- public WasmGCImageHeapLayoutInfo(long startOffset, long serializedSize, long theoreticalSize) {
- super(startOffset, theoreticalSize, 0, theoreticalSize, 0L, 0L, 0L, 0L);
+ public WasmGCImageHeapLayoutInfo(long serializedSize, long theoreticalSize) {
+ super(0, theoreticalSize, 0, theoreticalSize, 0L, 0L, 0L, 0L);
this.serializedSize = serializedSize;
}
+ @Override
+ protected boolean verifyAlignment() {
+ /* Ignore alignment. */
+ return true;
+ }
+
public long getSerializedSize() {
return serializedSize;
}
diff --git a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/heap/WebImageJSHeap.java b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/heap/WebImageJSHeap.java
index 937671723e7f..239b63507d0d 100644
--- a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/heap/WebImageJSHeap.java
+++ b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/heap/WebImageJSHeap.java
@@ -112,7 +112,7 @@ public int getClassCount() {
}
@Override
- protected List> getAllClasses() {
+ protected List> getClassesInImageHeap() {
return null;
}
@@ -139,8 +139,13 @@ public void endSafepoint() {
}
@Override
- public int getPreferredAddressSpaceAlignment() {
- return 0;
+ public int getHeapBaseAlignment() {
+ return 1;
+ }
+
+ @Override
+ public int getImageHeapAlignment() {
+ return 1;
}
@Override
@@ -230,16 +235,6 @@ public UnsignedWord getUsedMemoryAfterLastGC() {
return Word.zero();
}
- @Override
- public UnsignedWord getImageHeapReservedBytes() {
- throw VMError.unimplemented("Native Memory Tracking is not supported");
- }
-
- @Override
- public UnsignedWord getImageHeapCommittedBytes() {
- throw VMError.unimplemented("Native Memory Tracking is not supported");
- }
-
@Override
public void doReferenceHandling() {
}