Skip to content

Commit cf8e0b6

Browse files
committed
[GR-43225] [GR-43635] [GR-43632] Optional identity hash code field and GC policy improvements.
PullRequest: graal/13482
2 parents 3ee5810 + ae461b2 commit cf8e0b6

File tree

55 files changed

+833
-296
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+833
-296
lines changed

substratevm/mx.substratevm/testhello.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def __init__(self, name, regexps):
5757
self.name = name
5858
if not isinstance(regexps, list):
5959
regexps = [regexps]
60-
self.rexps = [re.compile(regexp) for regexp in regexps]
60+
self.rexps = [re.compile(r) for r in regexps if r is not None]
6161

6262
# Check that successive lines of a gdb command's output text
6363
# match the corresponding regexp patterns provided when this
@@ -180,6 +180,9 @@ def test():
180180
# disable printing of address symbols
181181
execute("set print symbol off")
182182

183+
hub_ref_size = int(execute("printf \"%d\", sizeof('java.lang.Object'::__hub__)"))
184+
fixed_idhash_field = (hub_ref_size > 4)
185+
183186
# Print DefaultGreeter and check the modifiers of its methods and fields
184187
exec_string = execute("ptype 'hello.Hello$DefaultGreeter'")
185188
rexp = [r"type = class hello\.Hello\$DefaultGreeter : public hello\.Hello\$Greeter {",
@@ -241,7 +244,7 @@ def test():
241244
r"%s<java.lang.Object> = {"%(spaces_pattern),
242245
r"%s<_objhdr> = {"%(spaces_pattern),
243246
r"%shub = %s,"%(spaces_pattern, address_pattern),
244-
r"%sidHash = %s"%(spaces_pattern, address_pattern),
247+
r"%sidHash = %s"%(spaces_pattern, address_pattern) if fixed_idhash_field else None,
245248
r"%s}, <No data fields>}, "%(spaces_pattern),
246249
r"%smembers of java\.lang\.String\[\]:"%(spaces_pattern),
247250
r"%slen = 0x0,"%(spaces_pattern),
@@ -260,7 +263,7 @@ def test():
260263
r"%s<java.lang.Object> = {"%(spaces_pattern),
261264
r"%s<_objhdr> = {"%(spaces_pattern),
262265
r"%shub = %s,"%(spaces_pattern, address_pattern),
263-
r"%sidHash = %s"%(spaces_pattern, address_pattern),
266+
r"%sidHash = %s"%(spaces_pattern, address_pattern) if fixed_idhash_field else None,
264267
r"%s}, <No data fields>},"%(spaces_pattern),
265268
r"%smembers of java\.lang\.Class:"%(spaces_pattern),
266269
r"%sname = %s,"%(spaces_pattern, address_pattern),
@@ -270,7 +273,7 @@ def test():
270273
r"%s<java.lang.Object> = {"%(spaces_pattern),
271274
r"%s<_objhdr> = {"%(spaces_pattern),
272275
r"%shub = %s,"%(spaces_pattern, address_pattern),
273-
r"%sidHash = %s"%(spaces_pattern, address_pattern),
276+
r"%sidHash = %s"%(spaces_pattern, address_pattern) if fixed_idhash_field else None,
274277
r"%s}, <No data fields>},"%(spaces_pattern),
275278
r"%smembers of java\.lang\.Class:"%(spaces_pattern),
276279
r"%sname = %s,"%(spaces_pattern, address_pattern),
@@ -310,7 +313,7 @@ def test():
310313
r"%s<java.lang.Object> = {"%(spaces_pattern),
311314
r"%s<_objhdr> = {"%(spaces_pattern),
312315
r"%shub = %s,"%(spaces_pattern, address_pattern),
313-
r"%sidHash = %s"%(spaces_pattern, address_pattern),
316+
r"%sidHash = %s"%(spaces_pattern, address_pattern) if fixed_idhash_field else None,
314317
r"%s}, <No data fields>},"%(spaces_pattern),
315318
r"%smembers of java\.lang\.Class:"%(spaces_pattern),
316319
r"%sname = %s,"%(spaces_pattern, address_pattern),
@@ -426,12 +429,12 @@ def test():
426429
if isolates:
427430
rexp = [r"type = struct _objhdr {",
428431
r"%s_z_\.java\.lang\.Class \*hub;"%(spaces_pattern),
429-
r"%sint idHash;"%(spaces_pattern),
432+
r"%sint idHash;"%(spaces_pattern) if fixed_idhash_field else None,
430433
r"}"]
431434
else:
432435
rexp = [r"type = struct _objhdr {",
433436
r"%sjava\.lang\.Class \*hub;"%(spaces_pattern),
434-
r"%sint idHash;"%(spaces_pattern),
437+
r"%sint idHash;"%(spaces_pattern) if fixed_idhash_field else None,
435438
r"}"]
436439

437440
checker = Checker('ptype _objhdr', rexp)

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,17 @@ public int getTenuringAge() {
263263
return tenuringThreshold;
264264
}
265265

266+
@Override
267+
public void onCollectionBegin(boolean completeCollection, long requestingNanoTime) {
268+
// Capture the fraction of bytes in aligned chunks at the start to include all allocated
269+
// (also dead) objects, because we use it to reserve aligned chunks for future allocations
270+
UnsignedWord youngChunkBytes = GCImpl.getGCImpl().getAccounting().getYoungChunkBytesBefore();
271+
if (youngChunkBytes.notEqual(0)) {
272+
UnsignedWord youngAlignedChunkBytes = HeapImpl.getHeapImpl().getYoungGeneration().getAlignedChunkBytes();
273+
avgYoungGenAlignedChunkFraction.sample(UnsignedUtils.toDouble(youngAlignedChunkBytes) / UnsignedUtils.toDouble(youngChunkBytes));
274+
}
275+
}
276+
266277
@Override
267278
public UnsignedWord getMinimumHeapSize() {
268279
return sizes.minHeapSize;

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,15 @@ class AdaptiveCollectionPolicy extends AbstractCollectionPolicy {
9494
* (disabled by default) which uses linear least-square fitting without discounting.
9595
*/
9696
private static final boolean ADAPTIVE_SIZE_USE_COST_ESTIMATORS = true;
97+
/** Number of space size adjustments before cost estimators are used. HotSpot default: 20. */
9798
private static final int ADAPTIVE_SIZE_POLICY_INITIALIZING_STEPS = ADAPTIVE_SIZE_POLICY_READY_THRESHOLD;
98-
/** The minimum increase in throughput in percent for expanding a space by 1% of its size. */
99-
private static final double ADAPTIVE_SIZE_ESTIMATOR_MIN_SIZE_COST_TRADEOFF = 0.5;
99+
/**
100+
* The minimum estimated decrease in {@link #gcCost()} in percent to decide in favor of
101+
* expanding a space by 1% of the combined size of {@link #edenSize} and {@link #promoSize}.
102+
*/
103+
private static final double ADAPTIVE_SIZE_ESTIMATOR_MIN_TOTAL_SIZE_COST_TRADEOFF = 0.5;
100104
/** The effective number of most recent data points used by estimator (exponential decay). */
101-
private static final int ADAPTIVE_SIZE_COST_ESTIMATORS_HISTORY_LENGTH = 12;
105+
private static final double ADAPTIVE_SIZE_COST_ESTIMATORS_HISTORY_LENGTH = 12;
102106
/** Threshold for triggering a complete collection after repeated minor collections. */
103107
private static final int CONSECUTIVE_MINOR_TO_MAJOR_COLLECTION_PAUSE_TIME_RATIO = 2;
104108
/**
@@ -240,7 +244,7 @@ private void computeSurvivorSpaceSizeAndThreshold(boolean isSurvivorOverflow, Un
240244
protected void computeEdenSpaceSize(@SuppressWarnings("unused") boolean completeCollection, @SuppressWarnings("unused") GCCause cause) {
241245
boolean expansionReducesCost = true; // general assumption
242246
if (shouldUseEstimator(youngGenChangeForMinorThroughput, minorGcCost())) {
243-
expansionReducesCost = expansionSignificantlyReducesCost(minorCostEstimator, edenSize);
247+
expansionReducesCost = expansionSignificantlyReducesTotalCost(minorCostEstimator, edenSize, majorGcCost(), promoSize);
244248
/*
245249
* Note that if the estimator thinks expanding does not lead to significant improvement,
246250
* shrink so to not get stuck in a supposed optimum and to keep collecting data points.
@@ -282,15 +286,17 @@ private static boolean shouldUseEstimator(long genChangeForThroughput, double co
282286
return ADAPTIVE_SIZE_USE_COST_ESTIMATORS && genChangeForThroughput > ADAPTIVE_SIZE_POLICY_INITIALIZING_STEPS && cost <= ADAPTIVE_SIZE_COST_ESTIMATOR_GC_COST_LIMIT;
283287
}
284288

285-
private static boolean expansionSignificantlyReducesCost(ReciprocalLeastSquareFit estimator, UnsignedWord size) {
289+
private static boolean expansionSignificantlyReducesTotalCost(ReciprocalLeastSquareFit estimator, UnsignedWord size, double otherCost, UnsignedWord otherSize) {
290+
double totalSize = UnsignedUtils.toDouble(size.add(otherSize));
286291
double x0 = UnsignedUtils.toDouble(size);
287292
double deltax = (1.01 - 1) * x0;
288-
if (deltax == 0) { // division by zero below
289-
return false;
293+
if (deltax == 0 || totalSize == 0) { // division by zero below
294+
return true; // general assumption for space expansion
290295
}
291-
double y0 = estimator.estimate(x0);
292-
double y1 = y0 * (1 - 0.01 * ADAPTIVE_SIZE_ESTIMATOR_MIN_SIZE_COST_TRADEOFF);
296+
double y0 = estimator.estimate(x0) + otherCost;
297+
double y1 = y0 * (1 - deltax / totalSize * ADAPTIVE_SIZE_ESTIMATOR_MIN_TOTAL_SIZE_COST_TRADEOFF);
293298
double minSlope = (y1 - y0) / deltax;
299+
294300
double estimatedSlope = estimator.getSlope(x0);
295301
return estimatedSlope <= minSlope;
296302
}
@@ -383,16 +389,10 @@ public void onCollectionBegin(boolean completeCollection, long requestingNanoTim
383389
latestMinorMutatorIntervalNanos = timer.getMeasuredNanos();
384390
}
385391

386-
// Capture the fraction of bytes in aligned chunks at the start to include all allocated
387-
// (also dead) objects, because we use it to reserve aligned chunks for future allocations
388-
UnsignedWord youngChunkBytes = GCImpl.getGCImpl().getAccounting().getYoungChunkBytesBefore();
389-
if (youngChunkBytes.notEqual(0)) {
390-
UnsignedWord youngAlignedChunkBytes = HeapImpl.getHeapImpl().getYoungGeneration().getAlignedChunkBytes();
391-
avgYoungGenAlignedChunkFraction.sample(UnsignedUtils.toDouble(youngAlignedChunkBytes) / UnsignedUtils.toDouble(youngChunkBytes));
392-
}
393-
394392
timer.reset();
395393
timer.open(); // measure collection pause
394+
395+
super.onCollectionBegin(completeCollection, requestingNanoTime);
396396
}
397397

398398
@Override
@@ -458,7 +458,7 @@ private void computeOldGenSpaceSize(UnsignedWord oldLive) { // compute_old_gen_f
458458

459459
boolean expansionReducesCost = true; // general assumption
460460
if (shouldUseEstimator(oldGenChangeForMajorThroughput, majorGcCost())) {
461-
expansionReducesCost = expansionSignificantlyReducesCost(majorCostEstimator, promoSize);
461+
expansionReducesCost = expansionSignificantlyReducesTotalCost(majorCostEstimator, promoSize, minorGcCost(), edenSize);
462462
/*
463463
* Note that if the estimator thinks expanding does not lead to significant improvement,
464464
* shrink so to not get stuck in a supposed optimum and to keep collecting data points.

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

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,34 @@
3939
class AdaptiveWeightedAverage {
4040
static final int OLD_THRESHOLD = 100;
4141

42-
private final int weight;
42+
/** @see #computeEffectiveHistoryLengthForWeight */
43+
static double computeWeightForEffectiveHistoryLength(double length) {
44+
assert length > 0;
45+
return 100.0 * (1.0 - Math.pow(Math.E, -1.0 / length));
46+
}
47+
48+
/**
49+
* Computes the effective history length for the given weight, which is the number of data
50+
* points after which the former history is discounted to 1/e, i.e., its time constant.
51+
*/
52+
static double computeEffectiveHistoryLengthForWeight(double weight) {
53+
assert weight > 0 && weight <= 100;
54+
return -1.0 / Math.log(1.0 - weight / 100.0);
55+
}
56+
57+
private final double weight;
4358

4459
private double average;
4560
private long sampleCount;
4661
private boolean isOld;
4762

48-
AdaptiveWeightedAverage(int weight) {
63+
/** @param weight Weight of newest sample in percent, from 0 (exclusive) to 100 (inclusive). */
64+
AdaptiveWeightedAverage(double weight) {
4965
this(weight, 0);
5066
}
5167

52-
AdaptiveWeightedAverage(int weight, double avg) {
68+
AdaptiveWeightedAverage(double weight, double avg) {
69+
assert weight > 0 && weight <= 100;
5370
this.weight = weight;
5471
this.average = avg;
5572
}
@@ -76,16 +93,16 @@ protected double computeAdaptiveAverage(double sample, double avg) {
7693
* it meaningful. We'd like the first weight used to be 1, the second to be 1/2, etc until
7794
* we have OLD_THRESHOLD/weight samples.
7895
*/
79-
long countWeight = 0;
96+
double countWeight = 0;
8097
if (!isOld) { // avoid division by zero if the counter wraps
81-
countWeight = OLD_THRESHOLD / sampleCount;
98+
countWeight = OLD_THRESHOLD / (double) sampleCount;
8299
}
83-
long adaptiveWeight = Math.max(weight, countWeight);
100+
double adaptiveWeight = Math.max(weight, countWeight);
84101
return expAvg(avg, sample, adaptiveWeight);
85102
}
86103

87-
private static double expAvg(double avg, double sample, long adaptiveWeight) {
88-
assert adaptiveWeight <= 100 : "weight must be a percentage";
104+
private static double expAvg(double avg, double sample, double adaptiveWeight) {
105+
assert adaptiveWeight > 0 && adaptiveWeight <= 100 : "weight must be a percentage";
89106
return (100.0 - adaptiveWeight) * avg / 100.0 + adaptiveWeight * sample / 100.0;
90107
}
91108
}
@@ -103,7 +120,7 @@ class AdaptivePaddedAverage extends AdaptiveWeightedAverage {
103120
private double paddedAverage;
104121
private double deviation;
105122

106-
AdaptivePaddedAverage(int weight, int padding) {
123+
AdaptivePaddedAverage(double weight, int padding) {
107124
this(weight, padding, false);
108125
}
109126

@@ -112,7 +129,7 @@ class AdaptivePaddedAverage extends AdaptiveWeightedAverage {
112129
* allowed to change. This is to prevent zero samples from drastically changing the
113130
* padded average.
114131
*/
115-
AdaptivePaddedAverage(int weight, int padding, boolean noZeroDeviations) {
132+
AdaptivePaddedAverage(double weight, int padding, boolean noZeroDeviations) {
116133
super(weight);
117134
this.padding = padding;
118135
this.noZeroDeviations = noZeroDeviations;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,13 @@ static UnsignedWord getCommittedObjectMemory(AlignedHeader that) {
117117
return HeapChunk.getEndOffset(that).subtract(getObjectsStartOffset());
118118
}
119119

120+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
120121
public static AlignedHeader getEnclosingChunk(Object obj) {
121122
Pointer ptr = Word.objectToUntrackedPointer(obj);
122123
return getEnclosingChunkFromObjectPointer(ptr);
123124
}
124125

126+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
125127
public static AlignedHeader getEnclosingChunkFromObjectPointer(Pointer ptr) {
126128
return (AlignedHeader) PointerUtils.roundDown(ptr, HeapParameters.getAlignedHeapChunkAlignment());
127129
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public long getUnallocatedBytes() {
179179
this.position = position;
180180

181181
/* Cache to prevent frequent lookups of the object layout from ImageSingletons. */
182-
minimumObjectSize = ConfigurationValues.getObjectLayout().getMinimumObjectSize();
182+
this.minimumObjectSize = ConfigurationValues.getObjectLayout().getMinImageHeapObjectSize();
183183
}
184184

185185
public long getPosition() {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public class ChunkedImageHeapPartition extends AbstractImageHeapPartition {
5757
this.hugeObjects = hugeObjects;
5858

5959
/* Cache to prevent frequent lookups of the object layout from ImageSingletons. */
60-
minimumObjectSize = ConfigurationValues.getObjectLayout().getMinimumObjectSize();
60+
this.minimumObjectSize = ConfigurationValues.getObjectLayout().getMinImageHeapObjectSize();
6161
}
6262

6363
boolean usesUnalignedObjects() {
@@ -134,7 +134,7 @@ private static NavigableMap<Long, Queue<ImageHeapObject>> createSortedObjectsMap
134134
for (ImageHeapObject obj : sorted) {
135135
long objSize = obj.getSize();
136136
if (objSize != currentObjectsSize) {
137-
assert objSize > currentObjectsSize && objSize >= ConfigurationValues.getObjectLayout().getMinimumObjectSize();
137+
assert objSize > currentObjectsSize && objSize >= ConfigurationValues.getObjectLayout().getMinImageHeapObjectSize();
138138
currentObjectsSize = objSize;
139139
currentQueue = new ArrayDeque<>();
140140
map.put(currentObjectsSize, currentQueue);

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

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.oracle.svm.core.heap.GCCause;
3333
import com.oracle.svm.core.heap.PhysicalMemory;
3434
import com.oracle.svm.core.util.UserError;
35+
import com.oracle.svm.util.ReflectionUtil;
3536

3637
/** The interface for a garbage collection policy. All sizes are in bytes. */
3738
public interface CollectionPolicy {
@@ -53,32 +54,38 @@ static String getInitialPolicyName() {
5354
@Platforms(Platform.HOSTED_ONLY.class)
5455
static CollectionPolicy getInitialPolicy() {
5556
String name = getInitialPolicyName();
57+
Class<? extends CollectionPolicy> clazz = getPolicyClass(name);
58+
return ReflectionUtil.newInstance(clazz);
59+
}
60+
61+
@Platforms(Platform.HOSTED_ONLY.class)
62+
static Class<? extends CollectionPolicy> getPolicyClass(String name) {
5663
switch (name) {
5764
case "Adaptive":
58-
return new AdaptiveCollectionPolicy();
65+
return AdaptiveCollectionPolicy.class;
5966
case "AggressiveShrink":
60-
return new AggressiveShrinkCollectionPolicy();
67+
return AggressiveShrinkCollectionPolicy.class;
6168
case "Proportionate":
62-
return new ProportionateSpacesPolicy();
69+
return ProportionateSpacesPolicy.class;
6370
case "BySpaceAndTime":
64-
return new BasicCollectionPolicies.BySpaceAndTime();
71+
return BasicCollectionPolicies.BySpaceAndTime.class;
6572
case "OnlyCompletely":
66-
return new BasicCollectionPolicies.OnlyCompletely();
73+
return BasicCollectionPolicies.OnlyCompletely.class;
6774
case "OnlyIncrementally":
68-
return new BasicCollectionPolicies.OnlyIncrementally();
75+
return BasicCollectionPolicies.OnlyIncrementally.class;
6976
case "NeverCollect":
70-
return new BasicCollectionPolicies.NeverCollect();
77+
return BasicCollectionPolicies.NeverCollect.class;
7178
}
7279
throw UserError.abort("Policy %s does not exist.", name);
7380
}
7481

7582
@Platforms(Platform.HOSTED_ONLY.class)
7683
static int getMaxSurvivorSpaces(Integer userValue) {
7784
String name = getInitialPolicyName();
78-
if ("Adaptive".equals(name) || "Proportionate".equals(name)) {
79-
return AbstractCollectionPolicy.getMaxSurvivorSpaces(userValue);
85+
if (BasicCollectionPolicies.BasicPolicy.class.isAssignableFrom(getPolicyClass(name))) {
86+
return BasicCollectionPolicies.getMaxSurvivorSpaces(userValue);
8087
}
81-
return BasicCollectionPolicies.getMaxSurvivorSpaces(userValue);
88+
return AbstractCollectionPolicy.getMaxSurvivorSpaces(userValue);
8289
}
8390

8491
static boolean shouldCollectYoungGenSeparately(boolean defaultValue) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@ private void collectOperation(CollectionVMOperationData data) {
196196
startCollectionOrExit();
197197

198198
timers.resetAllExceptMutator();
199-
collectionEpoch = collectionEpoch.add(1);
200199

201200
/* Flush all TLAB chunks to eden. */
202201
ThreadLocalAllocation.disableAndFlushForAllThreads();
@@ -358,7 +357,7 @@ private void printGCBefore(String cause) {
358357
Log verboseGCLog = Log.log();
359358
HeapImpl heap = HeapImpl.getHeapImpl();
360359
sizeBefore = ((SubstrateGCOptions.PrintGC.getValue() || SerialGCOptions.PrintHeapShape.getValue()) ? getChunkBytes() : WordFactory.zero());
361-
if (SubstrateGCOptions.VerboseGC.getValue() && getCollectionEpoch().equal(1)) {
360+
if (SubstrateGCOptions.VerboseGC.getValue() && getCollectionEpoch().equal(0)) {
362361
verboseGCLog.string("[Heap policy parameters: ").newline();
363362
verboseGCLog.string(" YoungGenerationSize: ").unsigned(getPolicy().getMaximumYoungGenerationSize()).newline();
364363
verboseGCLog.string(" MaximumHeapSize: ").unsigned(getPolicy().getMaximumHeapSize()).newline();
@@ -1151,6 +1150,7 @@ private void startCollectionOrExit() {
11511150

11521151
private void finishCollection() {
11531152
assert collectionInProgress;
1153+
collectionEpoch = collectionEpoch.add(1);
11541154
collectionInProgress = false;
11551155
}
11561156

0 commit comments

Comments
 (0)