1 /* 2 * Copyright (c) 2007, 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 nsk.share.gc.gp; 25 26 import java.io.PrintWriter; 27 import java.io.StringWriter; 28 import java.lang.invoke.*; 29 import java.util.*; 30 import jdk.test.whitebox.WhiteBox; 31 import nsk.share.gc.DefaultProducer; 32 import nsk.share.gc.gp.array.*; 33 import nsk.share.gc.gp.string.*; 34 import nsk.share.gc.gp.list.*; 35 import nsk.share.gc.gp.tree.*; 36 import nsk.share.gc.gp.misc.*; 37 import nsk.share.gc.gp.classload.*; 38 import nsk.share.gc.Memory; 39 import nsk.share.TestBug; 40 import nsk.share.test.*; 41 42 /** 43 * Utility methods for garbage producers. 44 */ 45 public final class GarbageUtils { 46 private static final int ALLOCATION_LIMIT = 50000000; //50 Mb 47 private static GarbageProducers garbageProducers; 48 private static List<GarbageProducer> primitiveArrayProducers; 49 private static List<GarbageProducer> arrayProducers; 50 private static final GarbageProducer defaultProducer = new DefaultProducer(); 51 public static enum OOM_TYPE { 52 ANY (), 53 HEAP("Java heap space"), 54 METASPACE("Metaspace", "Compressed class space"); 55 56 private final String[] expectedStrings; 57 OOM_TYPE(String... expectedStrings) { 58 this.expectedStrings = expectedStrings; 59 } 60 61 /** 62 * Returns true if the given error message matches 63 * one of expected strings. 64 */ 65 public boolean accept(String errorMessage) { 66 if (expectedStrings == null || expectedStrings.length == 0 || errorMessage == null) { 67 return true; 68 } 69 for (String s: expectedStrings) { 70 if (errorMessage.indexOf(s) != -1) { 71 return true; 72 } 73 } 74 return false; 75 } 76 }; 77 78 // Force loading of OOM_TYPE and calling of enum constructors when loading GarbageUtils class. 79 public static final Object[] thisIsGarbageArray_theOnlyPurposeForCreatingItAndDeclaringItPublicIsToInitializeIntancesOfOOMEnumberation = new Object[] { OOM_TYPE.ANY, OOM_TYPE.HEAP, OOM_TYPE.METASPACE }; 80 81 // Force early loading of classes that might otherwise unexpectedly fail 82 // class loading during testing due to high memory pressure. 83 public static final StringWriter preloadStringWriter = new StringWriter(1); 84 public static final PrintWriter preloadPrintWriter = new PrintWriter(preloadStringWriter); 85 public static final Throwable preloadThrowable = new Throwable("preload"); 86 87 private GarbageUtils() { 88 } 89 90 /** 91 * engages GC by allocating memory chunks and triggering youngGC. 92 * Allocations are done for a total of YOUNG_GC_ITERATIONS times. 93 * Each iteration, we allocate a memory chunk and trigger youngGC. 94 * Finally fullGC is run once. 95 * This way the objects get to travel to various GC regions. 96 * @param testMemory - memory size to be operated on 97 */ 98 public static void engageGC(long testMemory) { 99 final int YOUNG_GC_ITERATIONS = 100; 100 final long memChunk = testMemory / YOUNG_GC_ITERATIONS; 101 int iteration = 0; 102 Object referenceArray[] = new Object[YOUNG_GC_ITERATIONS]; 103 104 while (iteration < YOUNG_GC_ITERATIONS) { 105 referenceArray[iteration++] = defaultProducer.create(memChunk); 106 WhiteBox.getWhiteBox().youngGC(); 107 } 108 WhiteBox.getWhiteBox().fullGC(); 109 } 110 111 /** 112 * Eat memory using execution controller that waits for 2 minutes. 113 * @return number of OOME occured 114 */ 115 public static int eatMemory() { 116 return eatMemory(2 * 60 * 1000); 117 } 118 119 /** 120 * Eat memory using execution controller that waits for timeout. 121 * @return number of OOME occured 122 */ 123 public static int eatMemory(final long timeout) { 124 return eatMemory(new ExecutionController() { 125 final long initialTime = System.currentTimeMillis(); 126 127 @Override 128 public void start(long stdIterations) {} 129 130 @Override 131 public boolean iteration() {return false;} 132 133 @Override 134 public boolean continueExecution() { 135 return System.currentTimeMillis() - initialTime < timeout; 136 } 137 138 @Override 139 public long getIteration() {return 0;} 140 141 @Override 142 public void finish() {} 143 }); 144 } 145 146 147 /** 148 * Eat memory using given execution controller and garbage producer. 149 * 150 * @param stresser execution controller 151 * @param gp garbage producer 152 * @return number of OOME occured 153 */ 154 public static int eatMemory(ExecutionController stresser) { 155 return eatMemory(stresser, defaultProducer, 50, 100, 2, OOM_TYPE.ANY); 156 } 157 158 /** 159 * Eat memory using given execution controller and garbage producer. 160 * 161 * @param stresser execution controller 162 * @param gp garbage producer 163 * @return number of OOME occured 164 */ 165 public static int eatMemory(ExecutionController stresser, GarbageProducer gp) { 166 return eatMemory(stresser, gp, 50, 100, 2, OOM_TYPE.ANY); 167 } 168 169 /** 170 * Eat memory using given garbage producer and given factor. 171 * 172 * @param gp garbage producer 173 * @param factor factor to divide the array size by 174 * @return number of OOME occured 175 */ 176 public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long factor) { 177 return eatMemory(stresser, gp, 50, 100, factor, OOM_TYPE.ANY); 178 } 179 180 /** 181 * Eat memory using default(byte[]) garbage producer. 182 * 183 * Note that this method can throw Failure if any exception 184 * is thrown while eating memory. 185 * 186 * @param stresser stresser 187 * @param initialFactor determines which portion of initial memory initial chunk will be 188 * @param minMemoryChunk determines when to stop 189 * @param factor factor to divide the array size by 190 * @return number of OOME occured 191 */ 192 public static int eatMemory(ExecutionController stresser,long initialFactor, long minMemoryChunk, long factor) { 193 return eatMemory(stresser, defaultProducer, initialFactor, minMemoryChunk, factor, OOM_TYPE.ANY); 194 } 195 196 /** 197 * Eat memory using given garbage producer. 198 * 199 * Note that this method can throw Failure if any exception 200 * is thrown while eating memory. 201 * 202 * @param stresser stresser to use 203 * @param gp garbage producer 204 * @param initialFactor determines which portion of initial memory initial chunk will be 205 * @param minMemoryChunk determines when to stop 206 * @param factor factor to divide the array size by. A value of 0 means that method returns after first OOME 207 * @param type of OutOfMemory Exception: Java heap space or Metadata space 208 * @return number of OOME occured 209 */ 210 public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long initialFactor, long minMemoryChunk, long factor) { 211 return eatMemory(stresser, gp, initialFactor, minMemoryChunk, factor, OOM_TYPE.ANY); 212 } 213 214 static int numberOfOOMEs = 0; 215 216 /** 217 * Minimal wrapper of the main implementation. Catches any OOM 218 * that might be thrown when rematerializing Objects when deoptimizing. 219 * 220 * It is Important that the impl is not inlined. 221 */ 222 private static MethodType mt = MethodType.methodType( 223 int.class, 224 ExecutionController.class, 225 GarbageProducer.class, 226 long.class, 227 long.class, 228 long.class, 229 OOM_TYPE.class); 230 231 private static MethodHandle eat; 232 233 static { 234 try { 235 eat = MethodHandles.lookup().findStatic(GarbageUtils.class, "eatMemoryImpl", mt); 236 } catch (Exception nsme) { 237 // Can't run the test for some unexpected reason 238 throw new RuntimeException(nsme); 239 } 240 } 241 242 private static Throwable ultimateCause(Throwable t) { 243 while (t.getCause() != null) { 244 t = t.getCause(); 245 } 246 return t; 247 } 248 249 public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long initialFactor, long minMemoryChunk, long factor, OOM_TYPE type) { 250 try { 251 // Using a methodhandle invoke of eatMemoryImpl to prevent inlining of it 252 return (int) eat.invoke(stresser, gp, initialFactor, minMemoryChunk, factor, type); 253 } catch (OutOfMemoryError e) { 254 return numberOfOOMEs++; 255 } catch (Throwable t) { 256 if (ultimateCause(t) instanceof OutOfMemoryError) { 257 return numberOfOOMEs++; 258 } 259 throw new RuntimeException(t); 260 } 261 } 262 263 /** 264 * Eat memory using given garbage producer. 265 * 266 * Note that this method can throw Failure if any exception 267 * is thrown while eating memory. 268 * 269 * @param stresser stresser to use 270 * @param gp garbage producer 271 * @param initialFactor determines which portion of initial memory initial chunk will be 272 * @param minMemoryChunk determines when to stop 273 * @param factor factor to divide the array size by. A value of 0 means that method returns after first OOME 274 * @param type of OutOfMemory Exception: Java heap space or Metadata space 275 * @return number of OOME occured 276 */ 277 278 public static int eatMemoryImpl(ExecutionController stresser, GarbageProducer gp, long initialFactor, long minMemoryChunk, long factor, OOM_TYPE type) { 279 numberOfOOMEs = 0; 280 try { 281 byte[] someMemory = new byte[200000]; //200 Kb 282 try { 283 Runtime runtime = Runtime.getRuntime(); 284 long maxMemory = runtime.maxMemory(); 285 long maxMemoryChunk = maxMemory / initialFactor; 286 long chunk = maxMemoryChunk; 287 chunk = chunk > ALLOCATION_LIMIT ? ALLOCATION_LIMIT : chunk; 288 int allocations = 0; 289 List<Object> storage = new ArrayList<Object>(); 290 291 while (chunk > minMemoryChunk && stresser.continueExecution()) { 292 try { 293 storage.add(gp.create(chunk)); 294 if (Thread.currentThread().isInterrupted()) { 295 return numberOfOOMEs; 296 } 297 // if we are able to eat chunk*factor let 298 // try to increase size of chunk 299 if (chunk * factor < maxMemoryChunk 300 && factor != 0 && allocations++ == factor + 1) { 301 chunk = chunk * factor; 302 allocations = 0; 303 } 304 } catch (OutOfMemoryError e) { 305 someMemory = null; 306 if (type != OOM_TYPE.ANY) { 307 if (type.accept(e.toString())) { 308 numberOfOOMEs++; 309 } else { 310 // Trying to catch situation when Java generates OOM different type that test trying to catch 311 throw new TestBug("Test throw OOM of unexpected type." + e.toString()); 312 } 313 } else { 314 numberOfOOMEs++; 315 } 316 allocations = 0; 317 if (factor == 0) { 318 return numberOfOOMEs; 319 } else { 320 chunk = chunk / factor; 321 } 322 } 323 } 324 } catch (OutOfMemoryError e) { 325 someMemory = null; 326 if (type != OOM_TYPE.ANY) { 327 if (type.accept(e.toString())) { 328 numberOfOOMEs++; 329 } else { 330 // Trying to catch situation when Java generates OOM different type that test trying to catch 331 throw new TestBug("Test throw OOM of unexpected type." + e.toString()); 332 } 333 } else { 334 numberOfOOMEs++; 335 } 336 // all memory is eaten now even before we start, just return 337 } 338 } catch (OutOfMemoryError e) { 339 numberOfOOMEs++; 340 } 341 return numberOfOOMEs; 342 } 343 344 /** 345 * Get all primitive array producers. 346 */ 347 public static List<GarbageProducer> getPrimitiveArrayProducers() { 348 return getGarbageProducers().getPrimitiveArrayProducers(); 349 } 350 351 /** 352 * Get all array producers. 353 */ 354 public static List<GarbageProducer> getArrayProducers() { 355 return getGarbageProducers().getArrayProducers(); 356 } 357 358 /** 359 * Determine size of each object in array which will occupy given 360 * memory with distribution determined by given memory strategy. 361 */ 362 public static long getArraySize(long memory, MemoryStrategy memoryStrategy) { 363 return memoryStrategy.getSize(memory - Memory.getArrayExtraSize(), Memory.getReferenceSize()); 364 } 365 366 /** 367 * Determine object count in array which will occupy given 368 * memory with distribution determined by given memory strategy. 369 */ 370 public static int getArrayCount(long memory, MemoryStrategy memoryStrategy) { 371 return memoryStrategy.getCount(memory - Memory.getArrayExtraSize(), Memory.getReferenceSize()); 372 } 373 374 /** 375 * Get garbage producer by identifier. 376 * 377 * @param id identifier 378 * @return garbage producer for this identifier 379 */ 380 public static GarbageProducer getGarbageProducer(String id) { 381 if (id == null || id.equals("byteArr")) 382 return new ByteArrayProducer(); 383 else if (id.equals("booleanArr")) 384 return new BooleanArrayProducer(); 385 else if (id.equals("shortArr")) 386 return new ShortArrayProducer(); 387 else if (id.equals("charArr")) 388 return new CharArrayProducer(); 389 else if (id.equals("intArr")) 390 return new IntArrayProducer(); 391 else if (id.equals("longArr")) 392 return new LongArrayProducer(); 393 else if (id.equals("floatArr")) 394 return new FloatArrayProducer(); 395 else if (id.equals("doubleArr")) 396 return new DoubleArrayProducer(); 397 else if (id.equals("BooleanObjArr")) 398 return new BooleanObjArrayProducer(); 399 else if (id.equals("ByteObjArr")) 400 return new BooleanObjArrayProducer(); 401 else if (id.equals("IntegerObjArr")) 402 return new IntegerObjArrayProducer(); 403 else if (id.equals("objectArr")) 404 return new ObjectArrayProducer(); 405 else if (id.equals("randomString")) 406 return new RandomStringProducer(); 407 else if (id.equals("simpleString")) 408 return new SimpleStringProducer(); 409 else if (id.startsWith("interned(")) 410 return new InternedStringProducer(getGarbageProducer(getInBrackets(id))); 411 else if (id.startsWith("linearList(")) 412 return new LinearListProducer(MemoryStrategy.fromString(getInBrackets(id))); 413 else if (id.startsWith("circularList(")) 414 return new CircularListProducer(MemoryStrategy.fromString(getInBrackets(id))); 415 else if (id.startsWith("nonbranchyTree(")) 416 return new NonbranchyTreeProducer(MemoryStrategy.fromString(getInBrackets(id))); 417 else if (id.equals("class")) 418 return new GeneratedClassProducer(); 419 else if (id.startsWith("hashed(")) 420 return new HashedGarbageProducer(getGarbageProducer(getInBrackets(id))); 421 else if (id.startsWith("random(")) 422 return new RandomProducer(getGarbageProducerList(getInBrackets(id))); 423 else if (id.startsWith("twofields(")) 424 return new TwoFieldsObjectProducer(getGarbageProducer(getInBrackets(id))); 425 else if (id.startsWith("arrayof(")) 426 return new ArrayOfProducer(getGarbageProducer(getInBrackets(id))); 427 else if (id.startsWith("trace(")) 428 return new TraceProducer(getGarbageProducer(getInBrackets(id))); 429 else 430 throw new TestBug("Invalid garbage producer identifier: " + id); 431 } 432 433 private static String getInBrackets(String s) { 434 int n1 = s.indexOf('('); 435 if (n1 == -1) 436 throw new TestBug("Opening bracket not found: " + s); 437 int n2 = s.lastIndexOf(')'); 438 if (n2 == -1) 439 throw new TestBug("Closing bracket not found: " + s); 440 return s.substring(n1 + 1, n2); 441 } 442 443 private static List<GarbageProducer> getGarbageProducerList(String s) { 444 if (s.equals("primitiveArrays")) 445 return getPrimitiveArrayProducers(); 446 else if (s.equals("arrays")) 447 return getArrayProducers(); 448 else { 449 String[] ids = s.split(","); 450 List<GarbageProducer> garbageProducers = new ArrayList<GarbageProducer>(ids.length); 451 for (int i = 0; i < ids.length; ++i) 452 garbageProducers.add(getGarbageProducer(ids[i])); 453 return garbageProducers; 454 //throw new TestBug("Invalid id for list of garbage producers: " + id); 455 } 456 } 457 458 public static GarbageProducers getGarbageProducers() { 459 if (garbageProducers == null) 460 garbageProducers = new GarbageProducers(); 461 return garbageProducers; 462 } 463 } --- EOF ---