1 /* 2 * Copyright (c) 2016, 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 /* 25 * @test TestStressG1Humongous 26 * @key gc stress 27 * @summary Stress G1 by humongous allocations in situation near OOM 28 * @requires vm.gc.G1 29 * @requires !vm.flightRecorder 30 * @library /test/lib 31 * @modules java.base/jdk.internal.misc 32 * @run driver/timeout=1300 TestStressG1Humongous 33 */ 34 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Collections; 38 import java.util.concurrent.CountDownLatch; 39 import java.util.concurrent.atomic.AtomicInteger; 40 41 import jdk.test.lib.Platform; 42 import jdk.test.lib.Utils; 43 import jdk.test.lib.process.ProcessTools; 44 import jdk.test.lib.process.OutputAnalyzer; 45 46 public class TestStressG1Humongous{ 47 main(String[] args)48 public static void main(String[] args) throws Exception { 49 // Limit heap size on 32-bit platforms 50 int heapSize = Platform.is32bit() ? 512 : 1024; 51 // Heap size, region size, threads, humongous size, timeout 52 run(heapSize, 4, 3, 1.1, 120); 53 run(heapSize, 16, 5, 2.1, 120); 54 run(heapSize, 32, 4, 0.6, 120); 55 run(heapSize, 1, 7, 0.6, 600); 56 } 57 run(int heapSize, int regionSize, int threads, double humongousSize, int timeout)58 private static void run(int heapSize, int regionSize, int threads, double humongousSize, int timeout) 59 throws Exception { 60 ArrayList<String> options = new ArrayList<>(); 61 Collections.addAll(options, Utils.getTestJavaOpts()); 62 Collections.addAll(options, 63 "-Xlog:gc=debug", 64 "-Xmx" + heapSize + "m", 65 "-XX:+UseG1GC", 66 "-XX:G1HeapRegionSize=" + regionSize + "m", 67 "-Dtimeout=" + timeout, 68 "-Dthreads=" + threads, 69 "-Dhumongoussize=" + humongousSize, 70 "-Dregionsize=" + regionSize, 71 TestStressG1HumongousImpl.class.getName() 72 ); 73 ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(options.toArray(new String[options.size()])); 74 OutputAnalyzer output = new OutputAnalyzer(pb.start()); 75 output.shouldHaveExitValue(0); 76 } 77 } 78 79 class TestStressG1HumongousImpl { 80 // Timeout in seconds 81 private static final int TIMEOUT = Integer.getInteger("timeout", 60); 82 private static final int THREAD_COUNT = Integer.getInteger("threads", 2); 83 private static final int REGION_SIZE = Integer.getInteger("regionsize", 1) * 1024 * 1024; 84 private static final int HUMONGOUS_SIZE = (int) (REGION_SIZE * Double.parseDouble(System.getProperty("humongoussize", "1.5"))); 85 private static final int NUMBER_OF_FREE_REGIONS = 2; 86 87 private volatile boolean isRunning; 88 private final Thread[] threads; 89 private final AtomicInteger alocatedObjectsCount; 90 private CountDownLatch countDownLatch; 91 public static final List<Object> GARBAGE = Collections.synchronizedList(new ArrayList<>()); 92 main(String[] args)93 public static void main(String[] args) throws InterruptedException { 94 new TestStressG1HumongousImpl().run(); 95 } 96 TestStressG1HumongousImpl()97 public TestStressG1HumongousImpl() { 98 isRunning = true; 99 threads = new Thread[THREAD_COUNT]; 100 alocatedObjectsCount = new AtomicInteger(0); 101 } 102 run()103 private void run() throws InterruptedException { 104 new Thread(new Timer()).start(); 105 int checkedAmountOfHObjects = getExpectedAmountOfObjects(); 106 while (isRunning()) { 107 countDownLatch = new CountDownLatch(THREAD_COUNT); 108 startAllocationThreads(checkedAmountOfHObjects); 109 countDownLatch.await(); 110 GARBAGE.clear(); 111 System.out.println("Allocated " + alocatedObjectsCount.get() + " objects."); 112 alocatedObjectsCount.set(0); 113 } 114 System.out.println("Done!"); 115 } 116 117 /** 118 * Tries to fill available memory with humongous objects to get expected amount. 119 * @return expected amount of humongous objects 120 */ getExpectedAmountOfObjects()121 private int getExpectedAmountOfObjects() { 122 long maxMem = Runtime.getRuntime().maxMemory(); 123 int expectedHObjects = (int) (maxMem / HUMONGOUS_SIZE); 124 // Will allocate NUMBER_OF_FREE_REGIONS region less to give some free space for VM. 125 int checkedAmountOfHObjects = checkHeapCapacity(expectedHObjects) - NUMBER_OF_FREE_REGIONS; 126 if (checkedAmountOfHObjects <= 0) { 127 throw new RuntimeException("Cannot start testing because selected maximum heap " 128 + "is not large enough to contain more than " + NUMBER_OF_FREE_REGIONS + " regions"); 129 } 130 return checkedAmountOfHObjects; 131 } 132 133 /** 134 * Starts several threads to allocate the requested amount of humongous objects. 135 * @param totalObjects total amount of object that will be created 136 */ startAllocationThreads(int totalObjects)137 private void startAllocationThreads(int totalObjects) { 138 int objectsPerThread = totalObjects / THREAD_COUNT; 139 int objectsForLastThread = objectsPerThread + totalObjects % THREAD_COUNT; 140 for (int i = 0; i < THREAD_COUNT - 1; ++i) { 141 threads[i] = new Thread(new AllocationThread(countDownLatch, objectsPerThread, alocatedObjectsCount)); 142 } 143 threads[THREAD_COUNT - 1] = new Thread(new AllocationThread(countDownLatch, objectsForLastThread, alocatedObjectsCount)); 144 for (int i = 0; i < THREAD_COUNT; ++i) { 145 threads[i].start(); 146 } 147 } 148 149 /** 150 * Creates a humongous object of the predefined size. 151 */ createObject()152 private void createObject() { 153 GARBAGE.add(new byte[HUMONGOUS_SIZE]); 154 } 155 156 /** 157 * Tries to create the requested amount of humongous objects. 158 * In case of OOME, stops creating and cleans the created garbage. 159 * @param expectedObjects amount of objects based on heap size 160 * @return amount of created objects 161 */ checkHeapCapacity(int expectedObjects)162 private int checkHeapCapacity(int expectedObjects) { 163 int allocated = 0; 164 try { 165 while (isRunning() && allocated < expectedObjects) { 166 createObject(); 167 ++allocated; 168 } 169 } catch (OutOfMemoryError oome) { 170 GARBAGE.clear(); 171 } 172 return allocated; 173 } 174 setDone()175 private void setDone() { 176 isRunning = false; 177 } 178 isRunning()179 private boolean isRunning() { 180 return isRunning; 181 } 182 183 /** 184 * Thread which allocates requested amount of humongous objects. 185 */ 186 private class AllocationThread implements Runnable { 187 188 private final int totalObjects; 189 private final CountDownLatch cdl; 190 private final AtomicInteger allocationCounter; 191 192 /** 193 * Creates allocation thread 194 * @param cdl CountDownLatch 195 * @param objects amount of objects to allocate 196 * @param counter 197 */ AllocationThread(CountDownLatch cdl, int objects, AtomicInteger counter)198 public AllocationThread(CountDownLatch cdl, int objects, AtomicInteger counter) { 199 totalObjects = objects; 200 this.cdl = cdl; 201 allocationCounter = counter; 202 } 203 204 @Override run()205 public void run() { 206 int allocatedObjects = 0; 207 try { 208 while (isRunning && allocatedObjects < totalObjects) { 209 createObject(); 210 allocatedObjects++; 211 allocationCounter.incrementAndGet(); 212 } 213 214 } catch (OutOfMemoryError oome) { 215 GARBAGE.clear(); 216 System.out.print("OOME was caught."); 217 System.out.println(" Allocated in thread: " + allocatedObjects + " . Totally allocated: " + allocationCounter.get() + "."); 218 } finally { 219 cdl.countDown(); 220 } 221 } 222 } 223 224 /** 225 * Simple Runnable which waits TIMEOUT and sets isRunning to false. 226 */ 227 class Timer implements Runnable { 228 229 @Override run()230 public void run() { 231 try { 232 Thread.sleep(TIMEOUT * 1000); 233 } catch (InterruptedException ex) { 234 } 235 setDone(); 236 } 237 } 238 } 239