Skip to content

Commit 249ede3

Browse files
author
Arash Kazemi Koohbanani
committed
[GR-65896] Dynamic Heap Size Manager for Serial GC
PullRequest: graal/19634
2 parents 8306a52 + 7451cbd commit 249ede3

File tree

7 files changed

+141
-5
lines changed

7 files changed

+141
-5
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
349349
UnsignedWord heapSizeLimit = UnsignedUtils.max(alignDown(getHeapSizeLimit()), minAllSpaces);
350350

351351
UnsignedWord maxHeap;
352-
long optionMax = SubstrateGCOptions.MaxHeapSize.getValue();
352+
long optionMax = getMaximumHeapSizeOptionValue();
353353
if (optionMax > 0L) {
354354
maxHeap = Word.unsigned(optionMax);
355355
} else {
@@ -418,6 +418,10 @@ protected UnsignedWord getYoungSizeLimit(UnsignedWord maxHeap) {
418418
return maxHeap.subtract(minSpaceSize());
419419
}
420420

421+
protected long getMaximumHeapSizeOptionValue() {
422+
return SubstrateGCOptions.MaxHeapSize.getValue();
423+
}
424+
421425
protected UnsignedWord getInitialHeapSize() {
422426
return AbstractCollectionPolicy.INITIAL_HEAP_SIZE;
423427
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ static Class<? extends CollectionPolicy> getPolicyClass(String name) {
8080
return BasicCollectionPolicies.OnlyIncrementally.class;
8181
case "NeverCollect":
8282
return BasicCollectionPolicies.NeverCollect.class;
83+
case "Dynamic":
84+
return DynamicCollectionPolicy.class;
8385
}
8486
throw UserError.abort("Policy %s does not exist.", name);
8587
}
@@ -210,4 +212,9 @@ static boolean shouldCollectYoungGenSeparately(boolean defaultValue) {
210212

211213
/** Called before the end of a collection, in the safepoint operation. */
212214
void onCollectionEnd(boolean completeCollection, GCCause cause);
215+
216+
/** Can be overridden to recover from OOM. */
217+
default boolean isOutOfMemory(UnsignedWord usedBytes) {
218+
return usedBytes.aboveThan(getMaximumHeapSize());
219+
}
213220
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.genscavenge;
26+
27+
import org.graalvm.nativeimage.ImageSingletons;
28+
import org.graalvm.word.UnsignedWord;
29+
30+
import com.oracle.svm.core.SubstrateGCOptions;
31+
import com.oracle.svm.core.heap.DynamicHeapSizeManager;
32+
33+
/**
34+
* This class provides the implementation of a dynamic collection policy that calls into an instance
35+
* of {@link DynamicHeapSizeManager} if it exists, otherwise the implementation is equivalent to
36+
* {@link AdaptiveCollectionPolicy}.
37+
*/
38+
class DynamicCollectionPolicy extends AdaptiveCollectionPolicy {
39+
@Override
40+
public String getName() {
41+
return "dynamic";
42+
}
43+
44+
@Override
45+
protected long getMaximumHeapSizeOptionValue() {
46+
if (ImageSingletons.contains(DynamicHeapSizeManager.class)) {
47+
return ImageSingletons.lookup(DynamicHeapSizeManager.class).maxHeapSize().rawValue();
48+
}
49+
50+
return SubstrateGCOptions.MaxHeapSize.getValue();
51+
}
52+
53+
@Override
54+
public boolean isOutOfMemory(UnsignedWord usedBytes) {
55+
if (ImageSingletons.contains(DynamicHeapSizeManager.class)) {
56+
return ImageSingletons.lookup(DynamicHeapSizeManager.class).outOfMemory(usedBytes);
57+
}
58+
59+
return super.isOutOfMemory(usedBytes);
60+
}
61+
}

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -169,6 +169,9 @@ public void maybeCollectOnAllocation(UnsignedWord allocationSize) {
169169
} else if (getPolicy().shouldCollectOnAllocation()) {
170170
AllocationRequiringGCEvent.emit(getCollectionEpoch(), allocationSize);
171171
outOfMemory = collectWithoutAllocating(GenScavengeGCCause.OnAllocation, false);
172+
if (outOfMemory) {
173+
outOfMemory = getPolicy().isOutOfMemory(HeapImpl.getAccounting().getUsedBytes());
174+
}
172175
}
173176
if (outOfMemory) {
174177
throw OutOfMemoryUtil.heapSizeExceeded();
@@ -186,7 +189,11 @@ private void collect(GCCause cause, boolean forceFullGC) {
186189
if (!hasNeverCollectPolicy()) {
187190
boolean outOfMemory = collectWithoutAllocating(cause, forceFullGC);
188191
if (outOfMemory) {
189-
throw OutOfMemoryUtil.heapSizeExceeded();
192+
if (getPolicy().isOutOfMemory(HeapImpl.getAccounting().getUsedBytes())) {
193+
throw OutOfMemoryUtil.heapSizeExceeded();
194+
} else {
195+
GCImpl.getPolicy().updateSizeParameters();
196+
}
190197
}
191198
}
192199
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public static Object slowPathNewArrayLikeObject(Word objectHeader, int length, b
262262
* object is allocated and survives.
263263
*/
264264
GCImpl.getPolicy().ensureSizeParametersInitialized();
265-
if (size.aboveOrEqual(GCImpl.getPolicy().getMaximumHeapSize()) && !GCImpl.shouldIgnoreOutOfMemory()) {
265+
if (GCImpl.getPolicy().isOutOfMemory(size) && !GCImpl.shouldIgnoreOutOfMemory()) {
266266
OutOfMemoryError outOfMemoryError = new OutOfMemoryError("Array allocation too large.");
267267
throw OutOfMemoryUtil.reportOutOfMemoryError(outOfMemoryError);
268268
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.heap;
26+
27+
import static com.oracle.svm.core.heap.RestrictHeapAccess.Access.NO_ALLOCATION;
28+
29+
import org.graalvm.word.UnsignedWord;
30+
31+
import com.oracle.svm.core.SubstrateGCOptions;
32+
33+
/**
34+
* This interface defines the functions needed to implement a dynamic heap size manager, where the
35+
* Java heap size can be changed at runtime.
36+
*/
37+
public interface DynamicHeapSizeManager {
38+
/**
39+
* Retrieves the current max heap size, overrides {@link SubstrateGCOptions#MaxHeapSize}.
40+
*
41+
* @return max heap size
42+
*/
43+
@RestrictHeapAccess(access = NO_ALLOCATION, reason = "Called from allocation-free code paths.")
44+
UnsignedWord maxHeapSize();
45+
46+
/**
47+
* In case of OOM, try to request additional memory.
48+
*
49+
* @param bytes memory required
50+
* @return true if out of memory, false if max heap size was increased
51+
*/
52+
@RestrictHeapAccess(access = NO_ALLOCATION, reason = "Called from allocation-free code paths.")
53+
boolean outOfMemory(UnsignedWord bytes);
54+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpSupportImpl.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,16 @@ private class HeapDumpOperation extends NativeVMOperation {
201201
}
202202

203203
@Override
204-
@RestrictHeapAccess(access = NO_ALLOCATION, reason = "Heap dumping must not allocate.")
205204
protected void operate(NativeVMOperationData d) {
206205
HeapDumpVMOperationData data = (HeapDumpVMOperationData) d;
207206
if (data.getGCBefore()) {
208207
Heap.getHeap().getGC().collectCompletely(GCCause.HeapDump);
209208
}
209+
dumpHeap(data);
210+
}
210211

212+
@RestrictHeapAccess(access = NO_ALLOCATION, reason = "Heap dumping must not allocate.")
213+
private void dumpHeap(HeapDumpVMOperationData data) {
211214
try {
212215
boolean success = writer.dumpHeap(data.getRawFileDescriptor());
213216
data.setSuccess(success);

0 commit comments

Comments
 (0)