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