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