1 /* 2 * Copyright (c) 2014, 2026, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package gc.g1; 25 26 /* 27 * @test TestEagerReclaimHumongousRegions 28 * @bug 8051973 29 * @summary Test to make sure that eager reclaim of humongous objects correctly works. 30 * @requires vm.gc.G1 31 * @requires vm.debug 32 * @library /test/lib /testlibrary / 33 * @modules java.base/jdk.internal.misc 34 * java.management 35 * @enablePreview 36 * @build jdk.test.whitebox.WhiteBox 37 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 38 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI --enable-preview gc.g1.TestEagerReclaimHumongousRegions 39 */ 40 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.regex.Matcher; 44 import java.util.regex.MatchResult; 45 import java.util.regex.Pattern; 46 47 import jdk.test.lib.Asserts; 48 import jdk.test.lib.process.OutputAnalyzer; 49 import jdk.test.lib.process.ProcessTools; 50 import jdk.test.whitebox.WhiteBox; 51 52 public class TestEagerReclaimHumongousRegions { 53 private static final WhiteBox WB = WhiteBox.getWhiteBox(); 54 55 enum ArrayType { TYPE_ARRAY, OBJ_ARRAY, FLAT_TYPE_ARRAY, FLAT_OBJ_ARRAY } 56 enum ReferencePolicy { KEEP, DROP } 57 enum AllocationTiming { BEFORE_MARK_START, AFTER_MARK_START} 58 59 enum ExpectedState { 60 MARKED_CANDIDATE_RECLAIMED(true, true, true), 61 MARKED_CANDIDATE_NOT_RECLAIMED(true, true, false), 62 MARKED_NOTCANDIDATE_NOTRECLAIMED(true, false, false), 63 NOTMARKED_CANDIDATE_RECLAIMED(false, true, true), 64 NOTMARKED_CANDIDATE_NOTRECLAIMED(false, true, false), 65 NOTMARKED_NOTCANDIDATE_NOTRECLAIMED(false, false, false); 66 67 final boolean marked; 68 final boolean candidate; 69 final boolean reclaimed; 70 71 ExpectedState(boolean marked, boolean candidate, boolean reclaimed) { 72 this.marked = marked; 73 this.candidate = candidate; 74 this.reclaimed = reclaimed; 75 } 76 } 77 78 /** 79 * Run the helper VM, passing configuration arguments, simulating an application allocating some kind of humongous object at a 80 * point during the induced concurrent mark, and executing a young gc. 81 * 82 * @param type Whether the allocated humongous object should be a typeArray, or an objArray. 83 * @param refPolicy Drop the reference to the allocated object after reaching the given phase or keep. 84 * @param timing Allocate the humongous objects before or after reaching the given phase. 85 * @param phase The phase during concurrent mark to reach before triggering a young garbage collection. 86 * @return Returns the stdout of the VM. 87 */ 88 private static String runHelperVM(List<String> args, ArrayType type, ReferencePolicy refPolicy, AllocationTiming timing, String phase) throws Exception { 89 90 int arrayKind = type.ordinal(); 91 boolean keepReference = (refPolicy == ReferencePolicy.KEEP); 92 boolean allocateAfter = (timing == AllocationTiming.AFTER_MARK_START); 93 94 OutputAnalyzer output = ProcessTools.executeLimitedTestJava("-XX:+UseG1GC", 95 "-Xmx20M", 96 "-Xms20M", 97 "-XX:+UnlockDiagnosticVMOptions", 98 "-XX:+VerifyAfterGC", 99 "-Xbootclasspath/a:.", 100 "-Xlog:gc=debug,gc+humongous=debug", 101 "-XX:+UnlockDiagnosticVMOptions", 102 "-XX:+WhiteBoxAPI", 103 "--enable-preview", 104 TestEagerReclaimHumongousRegionsClearMarkBitsRunner.class.getName(), 105 String.valueOf(arrayKind), 106 String.valueOf(keepReference), 107 String.valueOf(allocateAfter), 108 phase); 109 110 String log = output.getStdout(); 111 System.out.println(log); 112 output.shouldHaveExitValue(0); 113 return log; 114 } 115 116 private static List<String> testArgs() throws Exception { 117 return List.of("-XX:+UseG1GC", 118 "-Xmx20M", 119 "-Xms20m", 120 "-XX:+UnlockDiagnosticVMOptions", 121 "-XX:+VerifyAfterGC", 122 "-Xbootclasspath/a:.", 123 "-Xlog:gc=debug,gc+humongous=debug", 124 "-XX:+UnlockDiagnosticVMOptions", 125 "-XX:+WhiteBoxAPI"); 126 } 127 128 private static String boolToInt(boolean value) { 129 return value ? "1" : "0"; 130 } 131 132 private static void verifyLog(String log, AllocationTiming timing, ExpectedState expected) { 133 // Find the log output indicating that the humongous object has been reclaimed, and marked and verify for the expected results. 134 // [0.351s][debug][gc,humongous] GC(3) Humongous region 2 (object size 4194320 @ 0x00000000fee00000) remset 0 code roots 0 marked 1 pinned count 0 reclaim candidate 1 type array 1 135 136 // Now check the result of the reclaim attempt. We are interested in the last such message (as mentioned above, we might get two). 137 String patternString = "gc,humongous.* marked (\\d) pin.*candidate (\\d)"; 138 Pattern pattern = Pattern.compile(patternString); 139 Matcher matcher = pattern.matcher(log); 140 141 List<MatchResult> found = new ArrayList<MatchResult>(); 142 while (matcher.find()) { 143 found.add(matcher.toMatchResult()); 144 } 145 146 Asserts.assertTrue(found.size() == 1 || found.size() == 2, "Unexpected number of log messages " + found.size()); 147 148 if (found.size() == 2) { 149 Asserts.assertTrue(timing == AllocationTiming.BEFORE_MARK_START, "Should only have two messages if allocating the object before mark start"); 150 MatchResult mr = found.removeFirst(); 151 Asserts.assertTrue(mr.group(1).equals(boolToInt(false)), "Should not be marked before mark start " + mr.group()); 152 Asserts.assertTrue(mr.group(2).equals(boolToInt(true)), "Should be candidate before mark start " + mr.group()); 153 } 154 155 MatchResult mr = found.removeFirst(); 156 Asserts.assertTrue(mr.group(1).equals(boolToInt(expected.marked)), "Expected that region was " + (expected.marked ? "" : "not ") + " marked but is " + mr.group()); 157 Asserts.assertTrue(mr.group(2).equals(boolToInt(expected.candidate)), "Expected that region was " + (expected.candidate ? "" : "not ") + " candidate but is " + mr.group()); 158 159 boolean reclaimed = Pattern.compile("Reclaimed humongous region .*").matcher(log).find(); 160 Asserts.assertTrue(expected.reclaimed == reclaimed, "Wrong log output reclaiming humongous region"); 161 } 162 163 private static void runTest(ArrayType type, 164 ReferencePolicy refPolicy, 165 AllocationTiming timing, 166 String phase, 167 ExpectedState expected) throws Exception { 168 List<String> vmArgs = testArgs(); 169 170 ArrayList<String> args = new ArrayList(vmArgs); 171 String log = runHelperVM(args, type, refPolicy, timing, phase); 172 verifyLog(log, timing, expected); 173 174 ArrayList<String> jfrArgs = new ArrayList(vmArgs); 175 jfrArgs.addLast("-XX:StartFlightRecording=settings=profile"); 176 String jfrLog = runHelperVM(jfrArgs, type, refPolicy, timing, phase); 177 verifyLog(jfrLog, timing, expected); 178 } 179 180 private static void runTestNoRefsFor(ArrayType ot) throws Exception { 181 System.out.println("Tests checking eager reclaim for when the object of type " + ot + " is allocated before mark start."); 182 runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED); 183 runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED); 184 runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED); 185 186 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_CANDIDATE_NOT_RECLAIMED); 187 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_NOT_RECLAIMED); 188 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED); 189 190 System.out.println("Tests checking eager reclaim for when the object of type " + ot + " is allocated after mark start."); 191 // 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. 192 runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED); 193 runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED); 194 runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED); 195 196 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED); 197 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED); 198 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED); 199 } 200 201 private static void runTestWithRefsFor(ArrayType ot) throws Exception { 202 System.out.println("Tests checking eager reclaim for when the object of type " + ot + " is allocated before mark start."); 203 runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_NOTCANDIDATE_NOTRECLAIMED); 204 runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_RECLAIMED); 205 runTest(ot, ReferencePolicy.DROP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED); 206 207 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.MARKED_NOTCANDIDATE_NOTRECLAIMED); 208 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.MARKED_CANDIDATE_NOT_RECLAIMED); 209 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.BEFORE_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED); 210 211 System.out.println("Tests checking eager reclaim for when the object of type " + ot + " is allocated after mark start."); 212 // 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. 213 runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED); 214 runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED); 215 runTest(ot, ReferencePolicy.DROP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_RECLAIMED); 216 217 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.BEFORE_MARKING_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED); 218 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_REBUILD_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED); 219 runTest(ot, ReferencePolicy.KEEP, AllocationTiming.AFTER_MARK_START, WB.G1_BEFORE_CLEANUP_COMPLETED, ExpectedState.NOTMARKED_CANDIDATE_NOTRECLAIMED); 220 } 221 222 public static void main(String[] args) throws Exception { 223 runTestNoRefsFor(ArrayType.TYPE_ARRAY); 224 runTestWithRefsFor(ArrayType.OBJ_ARRAY); 225 runTestNoRefsFor(ArrayType.FLAT_TYPE_ARRAY); 226 runTestWithRefsFor(ArrayType.FLAT_OBJ_ARRAY); 227 } 228 } 229 230 class TestEagerReclaimHumongousRegionsClearMarkBitsRunner { 231 value class RefValue { 232 Object o; 233 RefValue() { o = null; } 234 } 235 236 value class IntValue { 237 int i; 238 IntValue() { i = 0; } 239 } 240 241 private static final WhiteBox WB = WhiteBox.getWhiteBox(); 242 private static final int SIZE = 1024 * 1024; 243 244 private static Object allocateHumongousObj(int type) { 245 switch (type) { 246 case 0: return new int[SIZE]; 247 case 1: return new Object[SIZE]; 248 case 2: return new IntValue[SIZE]; 249 case 3: return new RefValue[SIZE]; 250 default: throw new RuntimeException("Unknown object type " + type); 251 } 252 } 253 254 public static void main(String[] args) throws Exception { 255 if (args.length != 4) { 256 throw new Exception("Invalid number of arguments " + args.length); 257 } 258 int arrayType = Integer.parseInt(args[0]); 259 boolean keepReference = Boolean.parseBoolean(args[1]); 260 boolean allocateAfter = Boolean.parseBoolean(args[2]); 261 String phase = args[3]; 262 263 System.out.println("arrayType: " + arrayType + " keepReference: " + keepReference + " allocateAfter " + allocateAfter + " phase: " + phase); 264 WB.fullGC(); 265 266 Object largeObj = null; // Allocated humongous object. 267 if (!allocateAfter) { 268 largeObj = allocateHumongousObj(arrayType); 269 } 270 271 WB.concurrentGCAcquireControl(); 272 WB.concurrentGCRunTo(phase); 273 274 System.out.println("Phase " + phase + " reached"); 275 276 if (allocateAfter) { 277 largeObj = allocateHumongousObj(arrayType); 278 } 279 280 if (!keepReference) { 281 largeObj = null; 282 } 283 WB.youngGC(); // May reclaim the humongous object. 284 285 WB.concurrentGCRunToIdle(); 286 287 System.out.println("Large object at " + largeObj); // Keepalive. 288 } 289 } --- EOF ---