1 /*
   2  * Copyright (c) 2003, 2018, 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  * @modules jdk.management
  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 a pool which which supports low memory detection
  86         ListIterator iter = pools.listIterator();
  87         while (iter.hasNext()) {
  88             MemoryPoolMXBean p = (MemoryPoolMXBean) iter.next();
  89             // Only check heap pools that support a usage threshold.
  90             // This is typically only the old generation space
  91             // since the other spaces are expected to get filled up.
  92             if (p.getType() == MemoryType.HEAP &&
  93                 p.isUsageThresholdSupported()) {
  94                 // In all collectors except G1, only the old generation supports a
  95                 // usage threshold. The G1 legacy mode "G1 Old Gen" also does. In
  96                 // G1 default mode, both the old space ("G1 Old Space": it's not
  97                 // really a generation in the non-G1 collector sense) and the
  98                 // humongous space ("G1 Humongous Space"), support a usage threshold.
  99                 // So, the following condition is true for all non-G1 old generations,
 100                 // for the G1 legacy old gen, and for the G1 default humongous space.
 101                 // It is not true for the G1 default old gen.
 102                 //
 103                 // We're allocating humongous objects in this test, so the G1 default
 104                 // mode "G1 Old Space" occupancy doesn't change, because humongous
 105                 // objects are allocated in the "G1 Humongous Space". If we allowed
 106                 // the G1 default mode "G1 Old Space", notification would never
 107                 // happen because no objects are allocated there.
 108                 if (!p.getName().equals("G1 Old Space")) {
 109                     mpool = p;
 110                     if (trace) {
 111                         System.out.println("Selected memory pool for low memory " +
 112                                            "detection.");
 113                         MemoryUtil.printMemoryPool(mpool);
 114                     }
 115                     break;
 116                 }
 117             }
 118         }
 119         if (mpool == null) {
 120             throw new RuntimeException("TEST FAILED: No heap pool found");
 121         }
 122 
 123         SensorListener listener = new SensorListener();
 124         NotificationEmitter emitter = (NotificationEmitter) mm;
 125         emitter.addNotificationListener(listener, null, null);
 126 
 127         Thread allocator = new AllocatorThread();
 128 
 129         // The chunk size needs to be larger than YOUNG_GEN_SIZE,
 130         // otherwise we will get intermittent failures when objects
 131         // end up in the young gen instead of the old gen.
 132         final long epsilon = 1024;
 133         chunkSize = YOUNG_GEN_SIZE + epsilon;
 134 
 135         // Now set threshold
 136         MemoryUsage mu = mpool.getUsage();
 137         newThreshold = mu.getUsed() + (chunkSize * NUM_CHUNKS);
 138 
 139         // Sanity check. Make sure the new threshold isn't too large.
 140         // Tweak the test if this fails.
 141         final long headRoom = chunkSize * 2;
 142         final long max = mu.getMax();
 143         if (max != -1 && newThreshold > max - headRoom) {
 144             throw new RuntimeException("TEST FAILED: newThreshold: " + newThreshold +
 145                     " is too near the maximum old gen size: " + max +
 146                     " used: " + mu.getUsed() + " headRoom: " + headRoom);
 147         }
 148 
 149         System.out.println("Setting threshold for " + mpool.getName() +
 150             " from " + mpool.getUsageThreshold() + " to " + newThreshold +
 151             ".  Current used = " + mu.getUsed());
 152         mpool.setUsageThreshold(newThreshold);
 153 
 154         if (mpool.getUsageThreshold() != newThreshold) {
 155             throw new RuntimeException("TEST FAILED: " +
 156                 "Threshold for Memory pool " + mpool.getName() +
 157                 "is " + mpool.getUsageThreshold() + " but expected to be" +
 158                 newThreshold);
 159         }
 160 
 161         // Start the AllocatorThread to continously allocate memory
 162         System.out.println("Starting an AllocatorThread to allocate memory.");
 163         allocator.start();
 164 
 165         try {
 166             allocator.join();
 167         } catch (InterruptedException e) {
 168             e.printStackTrace();
 169             System.out.println("Unexpected exception.");
 170             testFailed = true;
 171         }
 172 
 173         if (listenerInvoked == 0) {
 174             throw new RuntimeException("No listener is invoked");
 175         }
 176 
 177         if (testFailed)
 178             throw new RuntimeException("TEST FAILED.");
 179 
 180         System.out.println("Test passed.");
 181 
 182     }
 183 
 184     static class AllocatorThread extends Thread {
 185         private List objectPool = new ArrayList();
 186         public void run() {
 187             int iterations = 0;
 188             int numElements = (int) (chunkSize / 4); // minimal object size
 189             while (listenerInvoked == 0) {
 190                 iterations++;
 191                 if (trace) {
 192                     System.out.println("    Iteration " + iterations +
 193                         ": before allocation " +
 194                         mpool.getUsage().getUsed());
 195                 }
 196 
 197                 Object[] o = new Object[numElements];
 198                 if (iterations <= NUM_CHUNKS) {
 199                     // only hold a reference to the first NUM_CHUNKS
 200                     // allocated objects
 201                     objectPool.add(o);
 202                 }
 203 
 204                 if (trace) {
 205                     System.out.println("                " +
 206                         "  after allocation " +
 207                         mpool.getUsage().getUsed());
 208                 }
 209                 try {
 210                     Thread.sleep(100);
 211                 } catch (InterruptedException e) {
 212                     e.printStackTrace();
 213                     System.out.println("Unexpected exception.");
 214                     testFailed = true;
 215                 }
 216             }
 217 
 218             System.out.println("AllocatedThread finished memory allocation " +
 219                 " num_iteration = " + iterations);
 220         }
 221     }
 222 
 223 }