Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,40 +50,36 @@

public class ChunkedImageHeapLayouter implements ImageHeapLayouter {
/** A partition holding read-only objects. */
private static final int READ_ONLY_REGULAR = 0;
private static final int ALIGNED_READ_ONLY_REGULAR = 0;
/**
* A pseudo-partition used during image building to consolidate objects that contain relocatable
* references.
* A partition holding all read-only objects that contain relocatable references.
* <p>
* Collecting the relocations together means the dynamic linker has to operate on less of the
* image heap during image startup, and it means that less of the image heap has to be
* copied-on-write if the image heap is relocated in a new process.
* <p>
* A relocated reference is read-only once relocated, e.g., at runtime.
* A relocated reference is read-only once relocated, i.e., at runtime.
*/
private static final int READ_ONLY_RELOCATABLE = READ_ONLY_REGULAR + 1;
private static final int ALIGNED_READ_ONLY_RELOCATABLE = ALIGNED_READ_ONLY_REGULAR + 1;
/**
* A partition holding objects which must be patched at execution startup by our initialization
* code. This is currently only used within layered images.
*/
private static final int WRITABLE_PATCHED = READ_ONLY_RELOCATABLE + 1;
private static final int ALIGNED_WRITABLE_PATCHED = ALIGNED_READ_ONLY_RELOCATABLE + 1;
/** A partition holding writable objects. */
private static final int WRITABLE_REGULAR = WRITABLE_PATCHED + 1;
/** A partition holding very large writable objects with or without references. */
private static final int WRITABLE_HUGE = WRITABLE_REGULAR + 1;
/**
* A partition holding very large read-only objects with or without references, but never with
* relocatable references.
*/
private static final int READ_ONLY_HUGE = WRITABLE_HUGE + 1;
private static final int PARTITION_COUNT = READ_ONLY_HUGE + 1;
private static final int ALIGNED_WRITABLE_REGULAR = ALIGNED_WRITABLE_PATCHED + 1;
/** A partition holding very large writable objects. */
private static final int UNALIGNED_WRITABLE = ALIGNED_WRITABLE_REGULAR + 1;
/** A partition holding very large read-only objects, but never with relocatable references. */
private static final int UNALIGNED_READ_ONLY = UNALIGNED_WRITABLE + 1;
private static final int PARTITION_COUNT = UNALIGNED_READ_ONLY + 1;

private static final String ALIGNED_HEAP_CHUNK_OPTION = SubstrateOptionsParser.commandArgument(SerialAndEpsilonGCOptions.AlignedHeapChunkSize, "<2^n>");

private final ChunkedImageHeapPartition[] partitions;
private final ImageHeapInfo heapInfo;
private final long startOffset;
private final long hugeObjectThreshold;
private final long unalignedObjectSizeThreshold;
private ChunkedImageHeapAllocator allocator;

/** @param startOffset Offset relative to the heap base. */
Expand All @@ -92,19 +88,18 @@ 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);
this.partitions[WRITABLE_PATCHED] = new ChunkedImageHeapPartition("writablePatched", true, false);
this.partitions[WRITABLE_REGULAR] = new ChunkedImageHeapPartition("writable", true, false);
this.partitions[WRITABLE_HUGE] = new ChunkedImageHeapPartition("writableHuge", true, true);
this.partitions[READ_ONLY_HUGE] = new ChunkedImageHeapPartition("readOnlyHuge", false, true);
this.partitions[ALIGNED_READ_ONLY_REGULAR] = new ChunkedImageHeapPartition("alignedReadOnlyRegular", false, false);
this.partitions[ALIGNED_READ_ONLY_RELOCATABLE] = new ChunkedImageHeapPartition("alignedReadOnlyRelocatable", false, false);
this.partitions[ALIGNED_WRITABLE_PATCHED] = new ChunkedImageHeapPartition("alignedWritablePatched", true, false);
this.partitions[ALIGNED_WRITABLE_REGULAR] = new ChunkedImageHeapPartition("alignedWritableRegular", true, false);
this.partitions[UNALIGNED_WRITABLE] = new ChunkedImageHeapPartition("unalignedWritable", true, true);
this.partitions[UNALIGNED_READ_ONLY] = new ChunkedImageHeapPartition("unalignedReadOnly", false, true);

this.heapInfo = heapInfo;
this.startOffset = startOffset;

UnsignedWord alignedHeaderSize = RememberedSet.get().getHeaderSizeOfAlignedChunk();
UnsignedWord hugeThreshold = HeapParameters.getAlignedHeapChunkSize().subtract(alignedHeaderSize);
this.hugeObjectThreshold = hugeThreshold.rawValue();
this.unalignedObjectSizeThreshold = HeapParameters.getAlignedHeapChunkSize().subtract(alignedHeaderSize).rawValue();
}

@Override
Expand All @@ -124,35 +119,35 @@ private ChunkedImageHeapPartition choosePartition(ImageHeapObject info, boolean
throw VMError.shouldNotReachHere("Object cannot contain both relocatables and patched constants: " + info.getObject());
}
if (patched) {
return getWritablePatched();
return getAlignedWritablePatched();
} else if (immutable) {
if (info.getSize() >= hugeObjectThreshold) {
if (info.getSize() >= unalignedObjectSizeThreshold) {
if (hasRelocatables) {
if (info.getObjectClass() == DynamicHub.class) {
throw reportHugeObjectError(info, "Class metadata (dynamic hubs) cannot be huge objects: the dynamic hub %s", info.getObject().toString());
throw reportObjectTooLargeForAlignedChunkError(info, "Class metadata (dynamic hubs) cannot be in unaligned heap chunks: the dynamic hub %s", info.getObject().toString());
}
throw reportHugeObjectError(info, "Objects in image heap with relocatable pointers cannot be huge objects. Detected an object of type %s",
throw reportObjectTooLargeForAlignedChunkError(info, "Objects in image heap with relocatable pointers cannot be in unaligned heap chunks. Detected an object of type %s",
info.getObject().getClass().getTypeName());
}
return getReadOnlyHuge();
return getUnalignedReadOnly();
}
if (hasRelocatables) {
return getReadOnlyRelocatable();
return getAlignedReadOnlyRelocatable();
} else {
return getReadOnlyRegular();
return getAlignedReadOnlyRegular();
}
} else {
assert info.getObjectClass() != DynamicHub.class : "Class metadata (dynamic hubs) cannot be writable";
if (info.getSize() >= hugeObjectThreshold) {
return getWritableHuge();
if (info.getSize() >= unalignedObjectSizeThreshold) {
return getUnalignedWritable();
}
return getWritableRegular();
return getAlignedWritableRegular();
}
}

private Error reportHugeObjectError(ImageHeapObject info, String objectTypeMsg, String objectText) {
private Error reportObjectTooLargeForAlignedChunkError(ImageHeapObject info, String objectTypeMsg, String objectText) {
String msg = String.format(objectTypeMsg + " with size %d B and the limit is %d B. Use '%s' to increase GC chunk size to be larger than the object.",
objectText, info.getSize(), hugeObjectThreshold, ALIGNED_HEAP_CHUNK_OPTION);
objectText, info.getSize(), unalignedObjectSizeThreshold, ALIGNED_HEAP_CHUNK_OPTION);
if (ImageInfo.inImageBuildtimeCode()) {
throw UserError.abort(msg);
} else {
Expand Down Expand Up @@ -214,20 +209,37 @@ private ImageHeapLayoutInfo populateInfoObjects(int dynamicHubCount, int pageSiz
}
control.poll();

heapInfo.initialize(getReadOnlyRegular().firstObject, getReadOnlyRegular().lastObject, getReadOnlyRelocatable().firstObject, getReadOnlyRelocatable().lastObject,
getWritablePatched().firstObject, getWritablePatched().lastObject,
getWritableRegular().firstObject, getWritableRegular().lastObject, getWritableHuge().firstObject, getWritableHuge().lastObject,
getReadOnlyHuge().firstObject, getReadOnlyHuge().lastObject, offsetOfFirstWritableAlignedChunk, offsetOfFirstWritableUnalignedChunk, offsetOfLastWritableUnalignedChunk,
Object firstAlignedReadOnlyObject = firstNonNullValue(getAlignedReadOnlyRegular().firstObject, getAlignedReadOnlyRelocatable().firstObject);
Object lastAlignedReadOnlyObject = firstNonNullValue(getAlignedReadOnlyRelocatable().lastObject, getAlignedReadOnlyRegular().lastObject);
Object firstAlignedWritableObject = firstNonNullValue(getAlignedWritablePatched().firstObject, getAlignedWritableRegular().firstObject);
Object lastAlignedWritableObject = firstNonNullValue(getAlignedWritableRegular().lastObject, getAlignedWritablePatched().lastObject);

heapInfo.initialize(firstAlignedReadOnlyObject, lastAlignedReadOnlyObject,
getAlignedReadOnlyRelocatable().firstObject, getAlignedReadOnlyRelocatable().lastObject,
firstAlignedWritableObject, lastAlignedWritableObject,
getAlignedWritablePatched().firstObject, getAlignedWritablePatched().lastObject,
getUnalignedWritable().firstObject, getUnalignedWritable().lastObject,
getUnalignedReadOnly().firstObject, getUnalignedReadOnly().lastObject,
offsetOfFirstWritableAlignedChunk, offsetOfFirstWritableUnalignedChunk, offsetOfLastWritableUnalignedChunk,
dynamicHubCount);

control.poll();

long writableEnd = getWritableHuge().getStartOffset() + getWritableHuge().getSize();
long writableEnd = getUnalignedWritable().getStartOffset() + getUnalignedWritable().getSize();
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);
return new ImageHeapLayoutInfo(startOffset, imageHeapEnd, offsetOfFirstWritableAlignedChunk, writableSize, getReadOnlyRelocatable().getStartOffset(), getReadOnlyRelocatable().getSize(),
getWritablePatched().getStartOffset(), getWritablePatched().getSize());
long imageHeapEnd = NumUtil.roundUp(getUnalignedReadOnly().getStartOffset() + getUnalignedReadOnly().getSize(), pageSize);
return new ImageHeapLayoutInfo(startOffset, imageHeapEnd, offsetOfFirstWritableAlignedChunk, writableSize,
getAlignedReadOnlyRelocatable().getStartOffset(), getAlignedReadOnlyRelocatable().getSize(),
getAlignedWritablePatched().getStartOffset(), getAlignedWritablePatched().getSize());
}

private static Object firstNonNullValue(Object... objects) {
for (Object o : objects) {
if (o != null) {
return o;
}
}
return null;
}

@Override
Expand Down Expand Up @@ -266,27 +278,27 @@ private static void writeHeader(ImageHeapChunkWriter writer, Chunk previous, Chu
}
}

private ChunkedImageHeapPartition getReadOnlyRegular() {
return partitions[READ_ONLY_REGULAR];
private ChunkedImageHeapPartition getAlignedReadOnlyRegular() {
return partitions[ALIGNED_READ_ONLY_REGULAR];
}

private ChunkedImageHeapPartition getReadOnlyRelocatable() {
return partitions[READ_ONLY_RELOCATABLE];
private ChunkedImageHeapPartition getAlignedReadOnlyRelocatable() {
return partitions[ALIGNED_READ_ONLY_RELOCATABLE];
}

private ChunkedImageHeapPartition getWritablePatched() {
return partitions[WRITABLE_PATCHED];
private ChunkedImageHeapPartition getAlignedWritablePatched() {
return partitions[ALIGNED_WRITABLE_PATCHED];
}

private ChunkedImageHeapPartition getWritableRegular() {
return partitions[WRITABLE_REGULAR];
private ChunkedImageHeapPartition getAlignedWritableRegular() {
return partitions[ALIGNED_WRITABLE_REGULAR];
}

private ChunkedImageHeapPartition getWritableHuge() {
return partitions[WRITABLE_HUGE];
private ChunkedImageHeapPartition getUnalignedWritable() {
return partitions[UNALIGNED_WRITABLE];
}

private ChunkedImageHeapPartition getReadOnlyHuge() {
return partitions[READ_ONLY_HUGE];
private ChunkedImageHeapPartition getUnalignedReadOnly() {
return partitions[UNALIGNED_READ_ONLY];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
public class ChunkedImageHeapPartition implements ImageHeapPartition {
private final String name;
private final boolean writable;
private final boolean hugeObjects;
private final boolean unalignedChunks;
private final int minimumObjectSize;
private final List<ImageHeapObject> objects = new ArrayList<>();

Expand All @@ -55,10 +55,10 @@ public class ChunkedImageHeapPartition implements ImageHeapPartition {
long startOffset = -1;
long endOffset = -1;

ChunkedImageHeapPartition(String name, boolean writable, boolean hugeObjects) {
ChunkedImageHeapPartition(String name, boolean writable, boolean unalignedChunks) {
this.name = name;
this.writable = writable;
this.hugeObjects = hugeObjects;
this.unalignedChunks = unalignedChunks;

/* Cache to prevent frequent lookups of the object layout from ImageSingletons. */
this.minimumObjectSize = ConfigurationValues.getObjectLayout().getMinImageHeapObjectSize();
Expand All @@ -70,25 +70,26 @@ void assign(ImageHeapObject obj) {
}

void layout(ChunkedImageHeapAllocator allocator, ImageHeapLayouterControl control) {
if (hugeObjects) {
layoutInUnalignedChunks(allocator, control);
} else {
layoutInAlignedChunks(allocator, control);
}
}

private void layoutInUnalignedChunks(ChunkedImageHeapAllocator allocator, ImageHeapLayouterControl control) {
if (objects.isEmpty()) {
/*
* Without objects, don't force finishing the current chunk and therefore committing
* space for the rest of it. Another partition might be able to continue filling it, or,
* if no more objects follow, we don't need to dedicate space in the image at all.
* Without objects, there is no need to start a new chunk, or to force finishing the
* current chunk and therefore committing space for the rest of it. Another partition
* might be able to continue filling it, or, if no more objects follow, we don't need to
* dedicate space in the image at all.
*/
startOffset = allocator.getPosition();
endOffset = startOffset;
return;
}

if (unalignedChunks) {
layoutInUnalignedChunks(allocator, control);
} else {
layoutInAlignedChunks(allocator, control);
}
}

private void layoutInUnalignedChunks(ChunkedImageHeapAllocator allocator, ImageHeapLayouterControl control) {
allocator.finishAlignedChunk();
startOffset = allocator.getPosition();

Expand All @@ -108,7 +109,7 @@ private void layoutInAlignedChunks(ChunkedImageHeapAllocator allocator, ImageHea
}

private void allocateObjectsInAlignedChunks(ChunkedImageHeapAllocator allocator, ImageHeapLayouterControl control) {
NavigableMap<Long, Queue<ImageHeapObject>> sortedObjects = createSortedObjectsMap();
TreeMap<Long, Queue<ImageHeapObject>> sortedObjects = createSortedObjectsMap();
while (!sortedObjects.isEmpty()) {
ImageHeapObject info = dequeueBestFit(sortedObjects, allocator.getRemainingBytesInAlignedChunk());
if (info == null) {
Expand All @@ -120,16 +121,16 @@ private void allocateObjectsInAlignedChunks(ChunkedImageHeapAllocator allocator,
}
}

/**
* Find a floor entry. We intentionally do not call {@link TreeMap#getFloorEntry} because that
* method allocates a new entry object. Instead, we fetch the floor key and get the value for
* the returned key.
*/
private ImageHeapObject dequeueBestFit(NavigableMap<Long, Queue<ImageHeapObject>> sortedObjects, long nbytes) {
if (nbytes < minimumObjectSize) {
return null;
}

/**
* Find a floor entry. We are purposefully not calling {@link TreeMap#getFloorEntry(Object)}
* as that method allocates a new entry object. Instead, we fetch the floor key and get the
* value for the returned key.
*/
Long floorKey = sortedObjects.floorKey(nbytes);
if (floorKey == null) {
return null;
Expand All @@ -142,8 +143,8 @@ private ImageHeapObject dequeueBestFit(NavigableMap<Long, Queue<ImageHeapObject>
return obj;
}

private NavigableMap<Long, Queue<ImageHeapObject>> createSortedObjectsMap() {
NavigableMap<Long, Queue<ImageHeapObject>> map = new TreeMap<>();
private TreeMap<Long, Queue<ImageHeapObject>> createSortedObjectsMap() {
TreeMap<Long, Queue<ImageHeapObject>> map = new TreeMap<>();
for (ImageHeapObject obj : objects) {
long objSize = obj.getSize();
assert objSize >= ConfigurationValues.getObjectLayout().getMinImageHeapObjectSize() : Assertions.errorMessage(obj, objSize);
Expand Down Expand Up @@ -174,8 +175,8 @@ public boolean isWritable() {
return writable;
}

boolean usesUnalignedObjects() {
return hugeObjects;
boolean usesUnalignedChunks() {
return unalignedChunks;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -962,8 +962,8 @@ private void blackenImageHeapRoots(ImageHeapInfo imageHeapInfo) {
@AlwaysInline("GC Performance")
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
static void walkImageHeapRoots(ImageHeapInfo imageHeapInfo, ObjectVisitor visitor) {
ImageHeapWalker.walkPartitionInline(imageHeapInfo.firstWritableRegularObject, imageHeapInfo.lastWritableRegularObject, visitor, true);
ImageHeapWalker.walkPartitionInline(imageHeapInfo.firstWritableHugeObject, imageHeapInfo.lastWritableHugeObject, visitor, false);
ImageHeapWalker.walkPartitionInline(imageHeapInfo.firstAlignedWritableObject, imageHeapInfo.lastAlignedWritableObject, visitor, true);
ImageHeapWalker.walkPartitionInline(imageHeapInfo.firstUnalignedWritableObject, imageHeapInfo.lastUnalignedWritableObject, visitor, false);
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
Expand Down
Loading