1 /*
   2  * Copyright (c) 2003, 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
  26  * @bug     4530538 6980984
  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  * @author  Mandy Chung
  31  *
  32  * @modules jdk.management
  33  *
  34  * @build MemoryManagement MemoryUtil
  35  * @run main/othervm/timeout=600 -Xmn8m -XX:+IgnoreUnrecognizedVMOptions -XX:G1HeapRegionSize=1 -XX:-UseLargePages MemoryManagement
  36  */
  37 
  38 import java.lang.management.*;
  39 import java.util.*;
  40 import javax.management.*;
  41 import javax.management.openmbean.CompositeData;
  42 
  43 public class MemoryManagement {
  44     private static final MemoryMXBean mm = ManagementFactory.getMemoryMXBean();
  45     private static final List pools =
  46             Collections.synchronizedList(ManagementFactory.getMemoryPoolMXBeans());
  47     private static final List managers =
  48             Collections.synchronizedList(ManagementFactory.getMemoryManagerMXBeans());
  49     private static volatile MemoryPoolMXBean mpool = null;
  50     private static volatile boolean trace = false;
  51     private static volatile boolean testFailed = false;
  52     private static final int NUM_CHUNKS = 2;
  53     // Must match -Xmn set on the @run line
  54     private static final int YOUNG_GEN_SIZE = 8 * 1024 * 1024;
  55     private static volatile long chunkSize;
  56     private static volatile int listenerInvoked = 0;
  57 
  58     static class SensorListener implements NotificationListener {
  59         public void handleNotification(Notification notif, Object handback) {
  60             String type = notif.getType();
  61             if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED) ||
  62                 type.equals(MemoryNotificationInfo.
  63                     MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) {
  64 
  65                 MemoryNotificationInfo minfo = MemoryNotificationInfo.
  66                     from((CompositeData) notif.getUserData());
  67 
  68                 MemoryUtil.printMemoryNotificationInfo(minfo, type);
  69                 listenerInvoked++;
  70             }
  71         }
  72     }
  73 
  74     private static long newThreshold;
  75     public static void main(String args[]) throws Exception {
  76         if (args.length > 0 && args[0].equals("trace")) {
  77             trace = true;
  78         }
  79 
  80         if (trace) {
  81             MemoryUtil.printMemoryPools(pools);
  82             MemoryUtil.printMemoryManagers(managers);
  83         }
  84 
  85         // Find the Old generation which supports low memory detection
  86         ListIterator iter = pools.listIterator();
  87         while (iter.hasNext()) {
  88             MemoryPoolMXBean p = (MemoryPoolMXBean) iter.next();
  89             if (p.getType() == MemoryType.HEAP &&
  90                 p.isUsageThresholdSupported()) {
  91                 mpool = p;
  92                 if (trace) {
  93                     System.out.println("Selected memory pool for low memory " +
  94                         "detection.");
  95                     MemoryUtil.printMemoryPool(mpool);
  96                 }
  97                 break;
  98             }
  99         }
 100 
 101         SensorListener listener = new SensorListener();
 102         NotificationEmitter emitter = (NotificationEmitter) mm;
 103         emitter.addNotificationListener(listener, null, null);
 104 
 105         Thread allocator = new AllocatorThread();
 106 
 107         // The chunk size needs to be larger than YOUNG_GEN_SIZE,
 108         // otherwise we will get intermittent failures when objects
 109         // end up in the young gen instead of the old gen.
 110         final long epsilon = 1024;
 111         chunkSize = YOUNG_GEN_SIZE + epsilon;
 112 
 113         // Now set threshold
 114         MemoryUsage mu = mpool.getUsage();
 115         newThreshold = mu.getUsed() + (chunkSize * NUM_CHUNKS);
 116 
 117         // Sanity check. Make sure the new threshold isn't too large.
 118         // Tweak the test if this fails.
 119         final long headRoom = chunkSize * 2;
 120         final long max = mu.getMax();
 121         if (max != -1 && newThreshold > max - headRoom) {
 122             throw new RuntimeException("TEST FAILED: newThreshold: " + newThreshold +
 123                     " is too near the maximum old gen size: " + max +
 124                     " used: " + mu.getUsed() + " headRoom: " + headRoom);
 125         }
 126 
 127         System.out.println("Setting threshold for " + mpool.getName() +
 128             " from " + mpool.getUsageThreshold() + " to " + newThreshold +
 129             ".  Current used = " + mu.getUsed());
 130         mpool.setUsageThreshold(newThreshold);
 131 
 132         if (mpool.getUsageThreshold() != newThreshold) {
 133             throw new RuntimeException("TEST FAILED: " +
 134                 "Threshold for Memory pool " + mpool.getName() +
 135                 "is " + mpool.getUsageThreshold() + " but expected to be" +
 136                 newThreshold);
 137         }
 138 
 139         // Start the AllocatorThread to continously allocate memory
 140         System.out.println("Starting an AllocatorThread to allocate memory.");
 141         allocator.start();
 142 
 143         try {
 144             allocator.join();
 145         } catch (InterruptedException e) {
 146             e.printStackTrace();
 147             System.out.println("Unexpected exception.");
 148             testFailed = true;
 149         }
 150 
 151         if (listenerInvoked == 0) {
 152             throw new RuntimeException("No listener is invoked");
 153         }
 154 
 155         if (testFailed)
 156             throw new RuntimeException("TEST FAILED.");
 157 
 158         System.out.println("Test passed.");
 159 
 160     }
 161 
 162     static class AllocatorThread extends Thread {
 163         private List objectPool = new ArrayList();
 164         public void run() {
 165             int iterations = 0;
 166             int numElements = (int) (chunkSize / 4); // minimal object size
 167             while (listenerInvoked == 0) {
 168                 iterations++;
 169                 if (trace) {
 170                     System.out.println("    Iteration " + iterations +
 171                         ": before allocation " +
 172                         mpool.getUsage().getUsed());
 173                 }
 174 
 175                 Object[] o = new Object[numElements];
 176                 if (iterations <= NUM_CHUNKS) {
 177                     // only hold a reference to the first NUM_CHUNKS
 178                     // allocated objects
 179                     objectPool.add(o);
 180                 }
 181 
 182                 if (trace) {
 183                     System.out.println("                " +
 184                         "  after allocation " +
 185                         mpool.getUsage().getUsed());
 186                 }
 187                 try {
 188                     Thread.sleep(100);
 189                 } catch (InterruptedException e) {
 190                     e.printStackTrace();
 191                     System.out.println("Unexpected exception.");
 192                     testFailed = true;
 193                 }
 194             }
 195 
 196             System.out.println("AllocatedThread finished memory allocation " +
 197                 " num_iteration = " + iterations);
 198         }
 199     }
 200 
 201 }