1 /* 2 * Copyright (c) 2003, 2014, 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 26 * @bug 4530538 27 * @summary Basic unit test of memory management testing: 28 * 1) setUsageThreshold() and getUsageThreshold() 29 * 2) test low memory detection on the old generation. 30 * 31 * @author Mandy Chung 32 * 33 * @library /lib/testlibrary/ 34 * @build jdk.testlibrary.* LowMemoryTest MemoryUtil RunUtil 35 * @run main/timeout=600 LowMemoryTest 36 */ 37 38 import java.lang.management.*; 39 import java.util.*; 40 import java.util.concurrent.Phaser; 41 import javax.management.*; 42 import javax.management.openmbean.CompositeData; 43 44 public class LowMemoryTest { 45 private static final MemoryMXBean mm = ManagementFactory.getMemoryMXBean(); 46 private static final List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans(); 47 private static final Phaser phaser = new Phaser(2); 48 private static MemoryPoolMXBean mpool = null; 49 private static boolean trace = false; 50 private static boolean testFailed = false; 51 private static final int NUM_TRIGGERS = 5; 52 private static final int NUM_CHUNKS = 2; 53 private static final int YOUNG_GEN_SIZE = 8 * 1024 * 1024; 54 private static long chunkSize; 55 56 /** 57 * Run the test multiple times with different GC versions. 58 * First with default command line specified by the framework. 59 * Then with GC versions specified by the test. 60 */ 61 public static void main(String a[]) throws Throwable { 62 final String main = "LowMemoryTest$TestMain"; 63 // Use a low young gen size to ensure that the 64 // allocated objects are put in the old gen. 65 final String nmFlag = "-Xmn" + YOUNG_GEN_SIZE; 66 // Using large pages will change the young gen size, 67 // make sure we don't use them for this test. 68 final String lpFlag = "-XX:-UseLargePages"; 69 // Prevent G1 from selecting a large heap region size, 70 // since that would change the young gen size. 71 final String g1Flag = "-XX:G1HeapRegionSize=1m"; 72 //For filtering out -XX:+ExplicitGCInvokesConcurrent vm option 73 String[] filterExplicitGCOptions = new String[]{"-XX:\\+ExplicitGCInvokesConcurrent"}; 74 RunUtil.runTestClearGcOpts(filterExplicitGCOptions, main, nmFlag, lpFlag, "-XX:+UseSerialGC"); 75 RunUtil.runTestClearGcOpts(filterExplicitGCOptions, main, nmFlag, lpFlag, "-XX:+UseParallelGC"); 76 RunUtil.runTestClearGcOpts(filterExplicitGCOptions, main, nmFlag, lpFlag, "-XX:+UseG1GC", g1Flag); 77 RunUtil.runTestClearGcOpts(filterExplicitGCOptions, main, nmFlag, lpFlag, "-XX:+UseConcMarkSweepGC"); 78 } 79 80 private static volatile boolean listenerInvoked = false; 81 static class SensorListener implements NotificationListener { 82 @Override 83 public void handleNotification(Notification notif, Object handback) { 84 String type = notif.getType(); 85 if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED) || 86 type.equals(MemoryNotificationInfo. 87 MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) { 88 89 MemoryNotificationInfo minfo = MemoryNotificationInfo. 90 from((CompositeData) notif.getUserData()); 91 92 MemoryUtil.printMemoryNotificationInfo(minfo, type); 93 listenerInvoked = true; 94 } 95 } 96 } 97 98 static class TestListener implements NotificationListener { 99 private int triggers = 0; 100 private final long[] count = new long[NUM_TRIGGERS * 2]; 101 private final long[] usedMemory = new long[NUM_TRIGGERS * 2]; 102 @Override 103 public void handleNotification(Notification notif, Object handback) { 104 MemoryNotificationInfo minfo = MemoryNotificationInfo. 105 from((CompositeData) notif.getUserData()); 106 count[triggers] = minfo.getCount(); 107 usedMemory[triggers] = minfo.getUsage().getUsed(); 108 triggers++; 109 } 110 public void checkResult() throws Exception { 111 if (triggers != NUM_TRIGGERS) { 112 throw new RuntimeException("Unexpected number of triggers = " + 113 triggers + " but expected to be " + NUM_TRIGGERS); 114 } 115 116 for (int i = 0; i < triggers; i++) { 117 if (count[i] != i+1) { 118 throw new RuntimeException("Unexpected count of" + 119 " notification #" + i + 120 " count = " + count[i] + 121 " but expected to be " + (i+1)); 122 } 123 if (usedMemory[i] < newThreshold) { 124 throw new RuntimeException("Used memory = " + 125 usedMemory[i] + " is less than the threshold = " + 126 newThreshold); 127 } 128 } 129 } 130 } 131 132 private static long newThreshold; 133 134 private static class TestMain { 135 public static void main(String args[]) throws Exception { 136 if (args.length > 0 && args[0].equals("trace")) { 137 trace = true; 138 } 139 140 // Find the Old generation which supports low memory detection 141 ListIterator iter = pools.listIterator(); 142 while (iter.hasNext()) { 143 MemoryPoolMXBean p = (MemoryPoolMXBean) iter.next(); 144 if (p.getType() == MemoryType.HEAP && 145 p.isUsageThresholdSupported()) { 146 mpool = p; 147 if (trace) { 148 System.out.println("Selected memory pool for low memory " + 149 "detection."); 150 MemoryUtil.printMemoryPool(mpool); 151 } 152 break; 153 } 154 } 155 156 TestListener listener = new TestListener(); 157 SensorListener l2 = new SensorListener(); 158 NotificationEmitter emitter = (NotificationEmitter) mm; 159 emitter.addNotificationListener(listener, null, null); 160 emitter.addNotificationListener(l2, null, null); 161 162 Thread allocator = new AllocatorThread(); 163 Thread sweeper = new SweeperThread(); 164 165 // The chunk size needs to be larger than YOUNG_GEN_SIZE, 166 // otherwise we will get intermittent failures when objects 167 // end up in the young gen instead of the old gen. 168 final long epsilon = 1024; 169 chunkSize = YOUNG_GEN_SIZE + epsilon; 170 171 MemoryUsage mu = mpool.getUsage(); 172 newThreshold = mu.getUsed() + (chunkSize * NUM_CHUNKS); 173 174 // Sanity check. Make sure the new threshold isn't too large. 175 // Tweak the test if this fails. 176 final long headRoom = chunkSize * 2; 177 final long max = mu.getMax(); 178 if (max != -1 && newThreshold > max - headRoom) { 179 throw new RuntimeException("TEST FAILED: newThreshold: " + newThreshold + 180 " is too near the maximum old gen size: " + max + 181 " used: " + mu.getUsed() + " headRoom: " + headRoom); 182 } 183 184 System.out.println("Setting threshold for " + mpool.getName() + 185 " from " + mpool.getUsageThreshold() + " to " + newThreshold + 186 ". Current used = " + mu.getUsed()); 187 mpool.setUsageThreshold(newThreshold); 188 189 if (mpool.getUsageThreshold() != newThreshold) { 190 throw new RuntimeException("TEST FAILED: " + 191 "Threshold for Memory pool " + mpool.getName() + 192 "is " + mpool.getUsageThreshold() + " but expected to be" + 193 newThreshold); 194 } 195 196 197 allocator.start(); 198 // Force Allocator start first 199 phaser.arriveAndAwaitAdvance(); 200 sweeper.start(); 201 202 203 try { 204 allocator.join(); 205 // Wait until AllocatorThread's done 206 phaser.arriveAndAwaitAdvance(); 207 sweeper.join(); 208 } catch (InterruptedException e) { 209 System.out.println("Unexpected exception:" + e); 210 testFailed = true; 211 } 212 213 listener.checkResult(); 214 215 if (testFailed) 216 throw new RuntimeException("TEST FAILED."); 217 218 System.out.println(RunUtil.successMessage); 219 220 } 221 } 222 223 private static void goSleep(long ms) { 224 try { 225 Thread.sleep(ms); 226 } catch (InterruptedException e) { 227 System.out.println("Unexpected exception:" + e); 228 testFailed = true; 229 } 230 } 231 232 private static final List<Object> objectPool = new ArrayList<>(); 233 static class AllocatorThread extends Thread { 234 public void doTask() { 235 int iterations = 0; 236 int numElements = (int) (chunkSize / 4); // minimal object size 237 while (!listenerInvoked || mpool.getUsage().getUsed() < mpool.getUsageThreshold()) { 238 iterations++; 239 if (trace) { 240 System.out.println(" Iteration " + iterations + 241 ": before allocation " + 242 mpool.getUsage().getUsed()); 243 } 244 245 Object[] o = new Object[numElements]; 246 if (iterations <= NUM_CHUNKS) { 247 // only hold a reference to the first NUM_CHUNKS 248 // allocated objects 249 objectPool.add(o); 250 } 251 252 if (trace) { 253 System.out.println(" " + 254 " after allocation " + 255 mpool.getUsage().getUsed()); 256 } 257 goSleep(100); 258 } 259 } 260 @Override 261 public void run() { 262 for (int i = 1; i <= NUM_TRIGGERS; i++) { 263 // Sync with SweeperThread's second phase. 264 phaser.arriveAndAwaitAdvance(); 265 System.out.println("AllocatorThread is doing task " + i + 266 " phase " + phaser.getPhase()); 267 doTask(); 268 // Sync with SweeperThread's first phase. 269 phaser.arriveAndAwaitAdvance(); 270 System.out.println("AllocatorThread done task " + i + 271 " phase " + phaser.getPhase()); 272 if (testFailed) { 273 return; 274 } 275 } 276 } 277 } 278 279 static class SweeperThread extends Thread { 280 private void doTask() { 281 for (; mpool.getUsage().getUsed() >= 282 mpool.getUsageThreshold();) { 283 // clear all allocated objects and invoke GC 284 objectPool.clear(); 285 mm.gc(); 286 goSleep(100); 287 } 288 } 289 @Override 290 public void run() { 291 for (int i = 1; i <= NUM_TRIGGERS; i++) { 292 // Sync with AllocatorThread's first phase. 293 phaser.arriveAndAwaitAdvance(); 294 System.out.println("SweepThread is doing task " + i + 295 " phase " + phaser.getPhase()); 296 doTask(); 297 298 listenerInvoked = false; 299 300 // Sync with AllocatorThread's second phase. 301 phaser.arriveAndAwaitAdvance(); 302 System.out.println("SweepThread done task " + i + 303 " phase " + phaser.getPhase()); 304 if (testFailed) return; 305 } 306 } 307 } 308 }