< prev index next > test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegions.java
Print this page
/*
! * Copyright (c) 2014, 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.
/*
! * Copyright (c) 2014, 2026, 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.
* @requires vm.gc.G1
* @requires vm.debug
* @library /test/lib /testlibrary /
* @modules java.base/jdk.internal.misc
* java.management
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
! * @run main/othervm -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI gc.g1.TestEagerReclaimHumongousRegions
*/
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
* @requires vm.gc.G1
* @requires vm.debug
* @library /test/lib /testlibrary /
* @modules java.base/jdk.internal.misc
* java.management
+ * @enablePreview
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
! * @run main/othervm -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI --enable-preview gc.g1.TestEagerReclaimHumongousRegions
*/
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import jdk.test.whitebox.WhiteBox;
public class TestEagerReclaimHumongousRegions {
private static final WhiteBox WB = WhiteBox.getWhiteBox();
! enum ObjectType { TYPE_ARRAY, OBJ_ARRAY }
enum ReferencePolicy { KEEP, DROP }
enum AllocationTiming { BEFORE_MARK_START, AFTER_MARK_START}
enum ExpectedState {
MARKED_CANDIDATE_RECLAIMED(true, true, true),
import jdk.test.whitebox.WhiteBox;
public class TestEagerReclaimHumongousRegions {
private static final WhiteBox WB = WhiteBox.getWhiteBox();
! enum ArrayType { TYPE_ARRAY, OBJ_ARRAY, FLAT_TYPE_ARRAY, FLAT_OBJ_ARRAY }
enum ReferencePolicy { KEEP, DROP }
enum AllocationTiming { BEFORE_MARK_START, AFTER_MARK_START}
enum ExpectedState {
MARKED_CANDIDATE_RECLAIMED(true, true, true),
* @param refPolicy Drop the reference to the allocated object after reaching the given phase or keep.
* @param timing Allocate the humongous objects before or after reaching the given phase.
* @param phase The phase during concurrent mark to reach before triggering a young garbage collection.
* @return Returns the stdout of the VM.
*/
! private static String runHelperVM(List<String> args, ObjectType type, ReferencePolicy refPolicy, AllocationTiming timing, String phase) throws Exception {
! boolean useTypeArray = (type == ObjectType.TYPE_ARRAY);
boolean keepReference = (refPolicy == ReferencePolicy.KEEP);
boolean allocateAfter = (timing == AllocationTiming.AFTER_MARK_START);
! args.add(TestEagerReclaimHumongousRegionsClearMarkBitsRunner.class.getName());
! args.add(String.valueOf(useTypeArray));
! args.add(String.valueOf(keepReference));
! args.add(String.valueOf(allocateAfter));
! args.add(phase);
!
! OutputAnalyzer output = ProcessTools.executeLimitedTestJava(args);
String log = output.getStdout();
System.out.println(log);
output.shouldHaveExitValue(0);
return log;
* @param refPolicy Drop the reference to the allocated object after reaching the given phase or keep.
* @param timing Allocate the humongous objects before or after reaching the given phase.
* @param phase The phase during concurrent mark to reach before triggering a young garbage collection.
* @return Returns the stdout of the VM.
*/
! private static String runHelperVM(List<String> args, ArrayType type, ReferencePolicy refPolicy, AllocationTiming timing, String phase) throws Exception {
! int arrayKind = type.ordinal();
boolean keepReference = (refPolicy == ReferencePolicy.KEEP);
boolean allocateAfter = (timing == AllocationTiming.AFTER_MARK_START);
! OutputAnalyzer output = ProcessTools.executeLimitedTestJava("-XX:+UseG1GC",
! "-Xmx20M",
! "-Xms20M",
! "-XX:+UnlockDiagnosticVMOptions",
! "-XX:+VerifyAfterGC",
! "-Xbootclasspath/a:.",
! "-Xlog:gc=debug,gc+humongous=debug",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "--enable-preview",
+ TestEagerReclaimHumongousRegionsClearMarkBitsRunner.class.getName(),
+ String.valueOf(arrayKind),
+ String.valueOf(keepReference),
+ String.valueOf(allocateAfter),
+ phase);
String log = output.getStdout();
System.out.println(log);
output.shouldHaveExitValue(0);
return log;
boolean reclaimed = Pattern.compile("Reclaimed humongous region .*").matcher(log).find();
Asserts.assertTrue(expected.reclaimed == reclaimed, "Wrong log output reclaiming humongous region");
}
! private static void runTest(ObjectType type,
ReferencePolicy refPolicy,
AllocationTiming timing,
String phase,
ExpectedState expected) throws Exception {
List<String> vmArgs = testArgs();
boolean reclaimed = Pattern.compile("Reclaimed humongous region .*").matcher(log).find();
Asserts.assertTrue(expected.reclaimed == reclaimed, "Wrong log output reclaiming humongous region");
}
! private static void runTest(ArrayType type,
ReferencePolicy refPolicy,
AllocationTiming timing,
String phase,
ExpectedState expected) throws Exception {
List<String> vmArgs = testArgs();
jfrArgs.addLast("-XX:StartFlightRecording=settings=profile");
String jfrLog = runHelperVM(jfrArgs, type, refPolicy, timing, phase);
verifyLog(jfrLog, timing, expected);
}
! public static void main(String[] args) throws Exception {
! System.out.println("Tests checking eager reclaim for when the object is allocated before mark start.");
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_CANDIDATE_NOT_RECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_NOT_RECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_NOTCANDIDATE_NOTRECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_NOTCANDIDATE_NOTRECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_NOT_RECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! System.out.println("Tests checking eager reclaim for when the object is allocated after mark start.");
! // These must not be marked (as they were allocated after mark start), and they are always candidates. Reclamation depends on whether there is a reference.
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ObjectType.TYPE_ARRAY, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ObjectType.OBJ_ARRAY, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
}
}
class TestEagerReclaimHumongousRegionsClearMarkBitsRunner {
private static final WhiteBox WB = WhiteBox.getWhiteBox();
private static final int SIZE = 1024 * 1024;
! private static Object allocateHumongousObj(boolean useTypeArray) {
! if (useTypeArray) {
! return new int[SIZE];
! } else {
! return new Object[SIZE];
}
}
public static void main(String[] args) throws Exception {
if (args.length != 4) {
throw new Exception("Invalid number of arguments " + args.length);
}
! boolean useTypeArray = Boolean.parseBoolean(args[0]);
boolean keepReference = Boolean.parseBoolean(args[1]);
boolean allocateAfter = Boolean.parseBoolean(args[2]);
String phase = args[3];
! System.out.println("useTypeArray: " + useTypeArray + " keepReference: " + keepReference + " allocateAfter " + allocateAfter + " phase: " + phase);
WB.fullGC();
Object largeObj = null; // Allocated humongous object.
if (!allocateAfter) {
! largeObj = allocateHumongousObj(useTypeArray);
}
WB.concurrentGCAcquireControl();
WB.concurrentGCRunTo(phase);
System.out.println("Phase " + phase + " reached");
if (allocateAfter) {
! largeObj = allocateHumongousObj(useTypeArray);
}
if (!keepReference) {
largeObj = null;
}
jfrArgs.addLast("-XX:StartFlightRecording=settings=profile");
String jfrLog = runHelperVM(jfrArgs, type, refPolicy, timing, phase);
verifyLog(jfrLog, timing, expected);
}
! private static void runTestNoRefsFor(ArrayType ot) throws Exception {
! System.out.println("Tests checking eager reclaim for when the object of type " + ot + " is allocated before mark start.");
! runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED);
! runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED);
! runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_CANDIDATE_NOT_RECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_NOT_RECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! System.out.println("Tests checking eager reclaim for when the object of type " + ot + " is allocated after mark start.");
! // These must not be marked (as they were allocated after mark start), and they are always candidates. Reclamation depends on whether there is a reference.
! runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
+ runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
+ runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
+ }
! private static void runTestWithRefsFor(ArrayType ot) throws Exception {
! System.out.println("Tests checking eager reclaim for when the object of type " + ot + " is allocated before mark start.");
! runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_NOTCANDIDATE_NOTRECLAIMED);
! runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED);
! runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_NOTCANDIDATE_NOTRECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_NOT_RECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! System.out.println("Tests checking eager reclaim for when the object of type " + ot + " is allocated after mark start.");
! // These must not be marked (as they were allocated after mark start), and they are always candidates. Reclamation depends on whether there is a reference.
! runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
+ runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
+ runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
! runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED);
+ }
+
+ public static void main(String[] args) throws Exception {
+ runTestNoRefsFor(ArrayType.TYPE_ARRAY);
+ runTestWithRefsFor(ArrayType.OBJ_ARRAY);
+ runTestNoRefsFor(ArrayType.FLAT_TYPE_ARRAY);
+ runTestWithRefsFor(ArrayType.FLAT_OBJ_ARRAY);
}
}
class TestEagerReclaimHumongousRegionsClearMarkBitsRunner {
+ value class RefValue {
+ Object o;
+ RefValue() { o = null; }
+ }
+
+ value class IntValue {
+ int i;
+ IntValue() { i = 0; }
+ }
+
private static final WhiteBox WB = WhiteBox.getWhiteBox();
private static final int SIZE = 1024 * 1024;
! private static Object allocateHumongousObj(int type) {
! switch (type) {
! case 0: return new int[SIZE];
! case 1: return new Object[SIZE];
! case 2: return new IntValue[SIZE];
+ case 3: return new RefValue[SIZE];
+ default: throw new RuntimeException("Unknown object type " + type);
}
}
public static void main(String[] args) throws Exception {
if (args.length != 4) {
throw new Exception("Invalid number of arguments " + args.length);
}
! int arrayType = Integer.parseInt(args[0]);
boolean keepReference = Boolean.parseBoolean(args[1]);
boolean allocateAfter = Boolean.parseBoolean(args[2]);
String phase = args[3];
! System.out.println("arrayType: " + arrayType + " keepReference: " + keepReference + " allocateAfter " + allocateAfter + " phase: " + phase);
WB.fullGC();
Object largeObj = null; // Allocated humongous object.
if (!allocateAfter) {
! largeObj = allocateHumongousObj(arrayType);
}
WB.concurrentGCAcquireControl();
WB.concurrentGCRunTo(phase);
System.out.println("Phase " + phase + " reached");
if (allocateAfter) {
! largeObj = allocateHumongousObj(arrayType);
}
if (!keepReference) {
largeObj = null;
}
< prev index next >