Skip to content

Commit 98753b8

Browse files
committed
[GR-54022] Build first-object table and reset card table in compacting GC without an extra pass.
PullRequest: graal/17771
2 parents 56a2af6 + 1c31464 commit 98753b8

11 files changed

+70
-42
lines changed

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ public UnsignedWord getMaximumFreeAlignedChunksSize() {
297297
/*
298298
* Keep chunks ready for allocations in eden and for the survivor to-spaces during young
299299
* collections (although we might keep too many aligned chunks when large objects in
300-
* unallocated chunks are also allocated). We could alternatively return
300+
* unaligned chunks are also allocated). We could alternatively return
301301
* getCurrentHeapCapacity() to have chunks ready during full GCs as well.
302302
*/
303303
UnsignedWord total = edenSize.add(survivorSize);

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AdaptiveCollectionPolicy.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ public boolean shouldCollectCompletely(boolean followingIncrementalCollection) {
190190
return false;
191191
}
192192

193-
if (minorCountSinceMajorCollection * avgMinorPause.getAverage() >= CONSECUTIVE_MINOR_TO_MAJOR_COLLECTION_PAUSE_TIME_RATIO * avgMajorPause.getPaddedAverage()) {
193+
if (!SerialGCOptions.useCompactingOldGen() &&
194+
minorCountSinceMajorCollection * avgMinorPause.getAverage() >= CONSECUTIVE_MINOR_TO_MAJOR_COLLECTION_PAUSE_TIME_RATIO * avgMajorPause.getPaddedAverage()) {
194195
/*
195196
* When we do many incremental collections in a row because they reclaim sufficient
196197
* space, still trigger a complete collection when reaching a cumulative pause time

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompactingOldGeneration.java

+4-10
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ private void fixupReferencesBeforeCompaction(ChunkReleaser chunkReleaser, Timers
287287
try {
288288
AlignedHeapChunk.AlignedHeader aChunk = space.getFirstAlignedHeapChunk();
289289
while (aChunk.isNonNull()) {
290-
ObjectMoveInfo.walkObjects(aChunk, fixupVisitor);
290+
ObjectMoveInfo.walkObjectsForFixup(aChunk, fixupVisitor);
291291
aChunk = HeapChunk.getNext(aChunk);
292292
}
293293
} finally {
@@ -399,18 +399,12 @@ private void compact(Timers timers) {
399399

400400
Timer oldCompactionRememberedSetsTimer = timers.oldCompactionRememberedSets.start();
401401
try {
402+
// Clear the card tables (which currently contain brick tables).
403+
// The first-object tables have already been populated.
402404
chunk = space.getFirstAlignedHeapChunk();
403405
while (chunk.isNonNull()) {
404-
/*
405-
* Clears the card table (which currently contains the brick table) and updates the
406-
* first object table.
407-
*
408-
* GR-54022: we should be able to avoid this pass and build the first object tables
409-
* during planning and reset card tables once we detect that we are finished with a
410-
* chunk during compaction. The remembered set bits are already set after planning.
411-
*/
412406
if (!AlignedHeapChunk.isEmpty(chunk)) {
413-
RememberedSet.get().enableRememberedSetForChunk(chunk);
407+
RememberedSet.get().clearRememberedSet(chunk);
414408
} // empty chunks will be freed or reset before reuse, no need to reinitialize here
415409

416410
chunk = HeapChunk.getNext(chunk);

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ReciprocalLeastSquareFit.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
package com.oracle.svm.core.genscavenge;
2626

2727
/**
28-
* Least squares fitting on a data set to generate an equation y = b + a / x. Uses exponential decay
28+
* Least squares fitting on a data set to generate an equation y = a + b / x. Uses exponential decay
2929
* to assign a higher weight to newly added data points and effectively drop old data points without
3030
* keeping a history.
3131
*

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ private Object copyAlignedObject(Object originalObj) {
451451
if (SerialGCOptions.useCompactingOldGen() && GCImpl.getGCImpl().isCompleteCollection()) {
452452
/*
453453
* In a compacting complete collection, the remembered set bit is set already during
454-
* marking and the first object table is built later during compaction.
454+
* marking and the first object table is built later during fix-up.
455455
*/
456456
} else {
457457
/*

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/compacting/ObjectMoveInfo.java

+34-7
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626

2727
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
2828

29-
import com.oracle.svm.core.util.VMError;
30-
import jdk.graal.compiler.word.Word;
3129
import org.graalvm.word.Pointer;
3230
import org.graalvm.word.UnsignedWord;
3331

@@ -37,10 +35,14 @@
3735
import com.oracle.svm.core.genscavenge.AlignedHeapChunk;
3836
import com.oracle.svm.core.genscavenge.HeapChunk;
3937
import com.oracle.svm.core.genscavenge.ObjectHeaderImpl;
38+
import com.oracle.svm.core.genscavenge.remset.AlignedChunkRememberedSet;
4039
import com.oracle.svm.core.genscavenge.remset.BrickTable;
40+
import com.oracle.svm.core.genscavenge.remset.FirstObjectTable;
4141
import com.oracle.svm.core.hub.LayoutEncoding;
42+
import com.oracle.svm.core.util.VMError;
4243

4344
import jdk.graal.compiler.api.replacements.Fold;
45+
import jdk.graal.compiler.word.Word;
4446

4547
/**
4648
* {@link PlanningVisitor} decides where objects will be moved and uses the methods of this class to
@@ -165,21 +167,46 @@ static boolean useCompressedLayout() {
165167
* @see AlignedHeapChunk#walkObjects
166168
*/
167169
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
168-
public static void walkObjects(AlignedHeapChunk.AlignedHeader chunkHeader, ObjectFixupVisitor visitor) {
169-
Pointer p = AlignedHeapChunk.getObjectsStart(chunkHeader);
170+
public static void walkObjectsForFixup(AlignedHeapChunk.AlignedHeader chunk, ObjectFixupVisitor visitor) {
171+
FirstObjectTable.initializeTable(AlignedChunkRememberedSet.getFirstObjectTableStart(chunk), AlignedChunkRememberedSet.getFirstObjectTableSize());
172+
173+
Pointer p = AlignedHeapChunk.getObjectsStart(chunk);
170174
do {
171-
Pointer nextObjSeq = getNextObjectSeqAddress(p);
172-
Pointer objSeqEnd = p.add(getObjectSeqSize(p));
173-
assert objSeqEnd.belowOrEqual(HeapChunk.getTopPointer(chunkHeader));
175+
Pointer objSeq = p;
176+
Pointer nextObjSeq = getNextObjectSeqAddress(objSeq);
177+
Pointer objSeqNewAddress = getNewAddress(objSeq);
178+
AlignedHeapChunk.AlignedHeader objSeqNewChunk = AlignedHeapChunk.getEnclosingChunkFromObjectPointer(objSeqNewAddress);
179+
Pointer objSeqEnd = objSeq.add(getObjectSeqSize(objSeq));
180+
assert objSeqEnd.belowOrEqual(HeapChunk.getTopPointer(chunk));
174181
while (p.notEqual(objSeqEnd)) {
175182
assert p.belowThan(objSeqEnd);
176183
Object obj = p.toObject();
177184
UnsignedWord objSize = LayoutEncoding.getSizeFromObjectInlineInGC(obj);
185+
186+
/*
187+
* Add the object's new location to the first object table of the target chunk. Note
188+
* that we have already encountered that chunk and initialized its table earlier.
189+
*
190+
* Rebuilding the table is also required for swept chunks, where dead objects can
191+
* mean that another object is now the first object in a range.
192+
*/
193+
Pointer newAddress = objSeqNewAddress.add(p.subtract(objSeq));
194+
UnsignedWord offset = newAddress.subtract(AlignedHeapChunk.getObjectsStart(objSeqNewChunk));
195+
FirstObjectTable.setTableForObject(AlignedChunkRememberedSet.getFirstObjectTableStart(objSeqNewChunk), offset, offset.add(objSize));
196+
178197
if (!visitor.visitObjectInline(obj)) {
179198
throw VMError.shouldNotReachHereAtRuntime();
180199
}
200+
181201
p = p.add(objSize);
182202
}
203+
if (nextObjSeq.isNonNull() && chunk.getShouldSweepInsteadOfCompact()) {
204+
// We will write a filler object here, add the location to the first object table.
205+
assert p.belowThan(nextObjSeq);
206+
UnsignedWord offset = p.subtract(AlignedHeapChunk.getObjectsStart(chunk));
207+
UnsignedWord size = nextObjSeq.subtract(p);
208+
FirstObjectTable.setTableForObject(AlignedChunkRememberedSet.getFirstObjectTableStart(chunk), offset, offset.add(size));
209+
}
183210
p = nextObjSeq;
184211
} while (p.isNonNull());
185212
}

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/compacting/PlanningVisitor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public boolean visitChunk(AlignedHeapChunk.AlignedHeader chunk) {
7171
UnsignedWord brickIndex = Word.zero();
7272

7373
/* Initialize the move info structure at the chunk's object start location. */
74-
ObjectMoveInfo.setNewAddress(objSeq, allocPointer);
74+
ObjectMoveInfo.setNewAddress(objSeq, objSeq);
7575
ObjectMoveInfo.setObjectSeqSize(objSeq, Word.zero());
7676
ObjectMoveInfo.setNextObjectSeqOffset(objSeq, Word.zero());
7777

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/compacting/SweepingVisitor.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.graalvm.word.UnsignedWord;
3131

3232
import com.oracle.svm.core.config.ConfigurationValues;
33+
import com.oracle.svm.core.genscavenge.AlignedHeapChunk;
34+
import com.oracle.svm.core.genscavenge.HeapChunk;
3335
import com.oracle.svm.core.genscavenge.graal.nodes.FormatArrayNode;
3436
import com.oracle.svm.core.genscavenge.graal.nodes.FormatObjectNode;
3537
import com.oracle.svm.core.heap.FillerArray;
@@ -62,12 +64,15 @@ static int arrayBaseOffset() {
6264

6365
@Override
6466
public boolean visit(Pointer objSeq, UnsignedWord size, Pointer newAddress, Pointer nextObjSeq) {
67+
assert objSeq.equal(newAddress);
6568
if (nextObjSeq.isNonNull()) {
6669
Pointer gapStart = objSeq.add(size);
67-
assert gapStart.belowOrEqual(nextObjSeq);
68-
if (gapStart.notEqual(nextObjSeq)) {
69-
writeFillerObjectAt(gapStart, nextObjSeq.subtract(gapStart));
70-
}
70+
assert gapStart.belowThan(nextObjSeq);
71+
writeFillerObjectAt(gapStart, nextObjSeq.subtract(gapStart));
72+
// Note that we have already added first object table entries for fillers during fixup.
73+
} else {
74+
AlignedHeapChunk.AlignedHeader chunk = AlignedHeapChunk.getEnclosingChunkFromObjectPointer(objSeq);
75+
assert objSeq.add(size).equal(HeapChunk.getTopPointer(chunk));
7176
}
7277
return true;
7378
}

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/AlignedChunkRememberedSet.java

+11-11
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
import jdk.graal.compiler.replacements.nodes.AssertionNode;
5454
import jdk.graal.compiler.word.Word;
5555

56-
final class AlignedChunkRememberedSet {
56+
public final class AlignedChunkRememberedSet {
5757
private AlignedChunkRememberedSet() {
5858
}
5959

@@ -63,7 +63,7 @@ public static int wordSize() {
6363
}
6464

6565
@Fold
66-
public static UnsignedWord getHeaderSize() {
66+
static UnsignedWord getHeaderSize() {
6767
UnsignedWord headerSize = getFirstObjectTableLimitOffset();
6868
if (SerialGCOptions.useCompactingOldGen()) {
6969
// Compaction needs room for a ObjectMoveInfo structure before the first object.
@@ -74,7 +74,7 @@ public static UnsignedWord getHeaderSize() {
7474
}
7575

7676
@Platforms(Platform.HOSTED_ONLY.class)
77-
public static void enableRememberedSet(HostedByteBufferPointer chunk, int chunkPosition, List<ImageHeapObject> objects) {
77+
static void enableRememberedSet(HostedByteBufferPointer chunk, int chunkPosition, List<ImageHeapObject> objects) {
7878
// Completely clean the card table and the first object table.
7979
CardTable.cleanTable(getCardTableStart(chunk), getCardTableSize());
8080
FirstObjectTable.initializeTable(getFirstObjectTableStart(chunk), getFirstObjectTableSize());
@@ -94,7 +94,7 @@ public static void enableRememberedSet(HostedByteBufferPointer chunk, int chunkP
9494

9595
@AlwaysInline("GC performance")
9696
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
97-
public static void enableRememberedSetForObject(AlignedHeader chunk, Object obj, UnsignedWord objSize) {
97+
static void enableRememberedSetForObject(AlignedHeader chunk, Object obj, UnsignedWord objSize) {
9898
Pointer fotStart = getFirstObjectTableStart(chunk);
9999
Pointer objectsStart = AlignedHeapChunk.getObjectsStart(chunk);
100100

@@ -107,7 +107,7 @@ public static void enableRememberedSetForObject(AlignedHeader chunk, Object obj,
107107
}
108108

109109
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
110-
public static void enableRememberedSet(AlignedHeader chunk) {
110+
static void enableRememberedSet(AlignedHeader chunk) {
111111
// Completely clean the card table and the first object table as further objects may be
112112
// added later on to this chunk.
113113
CardTable.cleanTable(getCardTableStart(chunk), getCardTableSize());
@@ -124,7 +124,7 @@ public static void enableRememberedSet(AlignedHeader chunk) {
124124
}
125125

126126
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
127-
public static void clearRememberedSet(AlignedHeader chunk) {
127+
static void clearRememberedSet(AlignedHeader chunk) {
128128
CardTable.cleanTable(getCardTableStart(chunk), getCardTableSize());
129129
}
130130

@@ -133,7 +133,7 @@ public static void clearRememberedSet(AlignedHeader chunk) {
133133
* the post-write barrier.
134134
*/
135135
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
136-
public static void dirtyCardForObject(Object object, boolean verifyOnly) {
136+
static void dirtyCardForObject(Object object, boolean verifyOnly) {
137137
Pointer objectPointer = Word.objectToUntrackedPointer(object);
138138
AlignedHeader chunk = AlignedHeapChunk.getEnclosingChunkFromObjectPointer(objectPointer);
139139
Pointer cardTableStart = getCardTableStart(chunk);
@@ -146,7 +146,7 @@ public static void dirtyCardForObject(Object object, boolean verifyOnly) {
146146
}
147147

148148
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
149-
public static void walkDirtyObjects(AlignedHeader chunk, UninterruptibleObjectVisitor visitor, boolean clean) {
149+
static void walkDirtyObjects(AlignedHeader chunk, UninterruptibleObjectVisitor visitor, boolean clean) {
150150
Pointer objectsStart = AlignedHeapChunk.getObjectsStart(chunk);
151151
Pointer objectsLimit = HeapChunk.getTopPointer(chunk);
152152
UnsignedWord memorySize = objectsLimit.subtract(objectsStart);
@@ -212,7 +212,7 @@ private static void walkObjects(AlignedHeader chunk, Pointer start, Pointer end,
212212
}
213213
}
214214

215-
public static boolean verify(AlignedHeader chunk) {
215+
static boolean verify(AlignedHeader chunk) {
216216
boolean success = true;
217217
success &= CardTable.verify(getCardTableStart(chunk), getCardTableEnd(chunk), AlignedHeapChunk.getObjectsStart(chunk), HeapChunk.getTopPointer(chunk));
218218
success &= FirstObjectTable.verify(getFirstObjectTableStart(chunk), AlignedHeapChunk.getObjectsStart(chunk), HeapChunk.getTopPointer(chunk));
@@ -242,7 +242,7 @@ static UnsignedWord getCardTableSize() {
242242
}
243243

244244
@Fold
245-
static UnsignedWord getFirstObjectTableSize() {
245+
public static UnsignedWord getFirstObjectTableSize() {
246246
return getCardTableSize();
247247
}
248248

@@ -294,7 +294,7 @@ private static Pointer getCardTableEnd(AlignedHeader chunk) {
294294
}
295295

296296
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
297-
private static Pointer getFirstObjectTableStart(AlignedHeader chunk) {
297+
public static Pointer getFirstObjectTableStart(AlignedHeader chunk) {
298298
return getFirstObjectTableStart(HeapChunk.asPointer(chunk));
299299
}
300300

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/BrickTable.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
/**
4141
* Inspired by the .NET CoreCLR GC, the {@link BrickTable} speeds up lookups of new object locations
4242
* after compaction by acting as a lookup table for {@link ObjectMoveInfo} structures. Each entry
43-
* stores a pointer to the start of the first such structure for the fraction of the chunk that it
43+
* stores the offset of the start of the first such structure for the fraction of the chunk that it
4444
* covers. It borrows the memory of a chunk's {@link CardTable}.
4545
*/
4646
public final class BrickTable {

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/FirstObjectTable.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
*/
2525
package com.oracle.svm.core.genscavenge.remset;
2626

27-
import jdk.graal.compiler.word.Word;
2827
import org.graalvm.word.Pointer;
2928
import org.graalvm.word.UnsignedWord;
3029

@@ -37,6 +36,8 @@
3736
import com.oracle.svm.core.log.Log;
3837
import com.oracle.svm.core.util.UnsignedUtils;
3938

39+
import jdk.graal.compiler.word.Word;
40+
4041
/**
4142
* A "first object table" to tell me the start of the first object that crosses onto a card
4243
* remembered set memory region.
@@ -109,7 +110,7 @@
109110
* <p>
110111
* Implementation note: Table entries are bytes but converted to and from ints with bounds checks.
111112
*/
112-
final class FirstObjectTable {
113+
public final class FirstObjectTable {
113114
/**
114115
* The number of bytes of memory covered by an entry. Since the indexes into the CardTable are
115116
* used to index into the FirstObjectTable, these need to have the same value.
@@ -237,7 +238,7 @@ public static void setTableForObject(Pointer table, UnsignedWord startOffset, Un
237238
*/
238239
@AlwaysInline("GC performance")
239240
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
240-
public static Pointer getFirstObjectImprecise(Pointer tableStart, Pointer objectsStart, UnsignedWord index) {
241+
static Pointer getFirstObjectImprecise(Pointer tableStart, Pointer objectsStart, UnsignedWord index) {
241242
Pointer result;
242243
Pointer firstObject = getFirstObject(tableStart, objectsStart, index);
243244
Pointer indexedMemoryStart = objectsStart.add(indexToMemoryOffset(index));
@@ -295,7 +296,7 @@ private static UnsignedWord entryToMemoryOffset(UnsignedWord index, int entry) {
295296
return indexOffset.subtract(entryOffset);
296297
}
297298

298-
public static boolean verify(Pointer tableStart, Pointer objectsStart, Pointer objectsLimit) {
299+
static boolean verify(Pointer tableStart, Pointer objectsStart, Pointer objectsLimit) {
299300
UnsignedWord indexLimit = getTableSizeForMemoryRange(objectsStart, objectsLimit);
300301
for (UnsignedWord index = Word.unsigned(0); index.belowThan(indexLimit); index = index.add(1)) {
301302
Pointer objStart = getFirstObject(tableStart, objectsStart, index);

0 commit comments

Comments
 (0)