1 /*
   2  * Copyright (c) 2013, 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 package metaspace.gc;
  25 
  26 import java.util.Arrays;
  27 import vm.share.VMRuntimeEnvUtils;
  28 
  29 /**
  30  * Test metaspace ergonomic.
  31  *
  32  * <ul>
  33  * <li>MetaspaceSize
  34  * <li>MaxMetaspaceSize
  35  * <li>MinMetaspaceFreeRatio
  36  * <li>MaxMetaspaceFreeRatio
  37  * </ul>
  38  *
  39  * The test loads classes until the committed metaspace achieves the certain
  40  * level between MetaspaceSize and MaxMetaspaceSize.
  41  * Then it counts how many times GC has been induced.
  42  * Test verifies that MinMetaspaceFreeRatio/MaxMetaspaceFreeRatio settings
  43  * affect the frequency of GC. (High-water mark)
  44  *
  45  * Note: The test doesn't check the GC count if CMS is used.
  46  *
  47  * Quoting: Java SE 8 HotSpot[tm] Virtual Machine Garbage Collection Tuning
  48  * <pre>
  49  * Class metadata is deallocated when the corresponding Java class is unloaded.
  50  * Java classes are unloaded as a results of garbage collection and garbage
  51  * collections may be induced in order to unload classes and deallocate class
  52  * metadata. When the space used for class metadata reaches a certain level
  53  * (call it a high-water mark), a garbage collection is induced.
  54  * After the garbage collection the high-water mark may be raised or lowered
  55  * depending on the amount of space freed from class metadata. The high-water
  56  * mark would be raised so as not to induce another garbage collection too soon.
  57  * The high-water mark is initially set to the value of the command-line
  58  * flag MetaspaceSize . It is raised or lowered based on the flags
  59  * MaxMetaspaceFreeRatio and MinMetaspaceFreeRatio.
  60  * If the committed space available for class metadata as a percentage of
  61  * the total committed space for class metadata is greater than
  62  * MaxMetaspaceFreeRatio, the high-water mark will be lowered.
  63  * If it is less than MinMetaspaceFreeRatio, the high-water mark will be raised.
  64  * </pre>
  65  */
  66 public class HighWaterMarkTest extends FirstGCTest {
  67 
  68     public static void main(String... args) {
  69         new HighWaterMarkTest().run(args);
  70     }
  71 
  72     // value given in -XX:MetaspaceSize=<value>
  73     private long metaspaceSize = -1;
  74 
  75     // value given in -XX:MaxMetaspaceSize=<value>
  76     private long maxMetaspaceSize = -1;
  77 
  78     // value given in -XX:MinMetaspaceFreeRatio=<value>
  79     private long minMetaspaceFreeRatio = -1;
  80 
  81     // value given in -XX:MaxMetaspaceFreeRatio=<value>
  82     private long maxMetaspaceFreeRatio = -1;
  83 
  84     /**
  85      * Parses arguments and vm options.
  86      * Throws Fault in cases of wrong values or missed parameters.
  87      *
  88      * @param args command line options
  89      */
  90     @Override
  91     protected void parseArgs(String[] args) {
  92         if (args.length > 0) {
  93             printUsage();
  94             throw new Fault("Illegal arguments: " + Arrays.asList(args));
  95         }
  96 
  97         if (gclogFileName == null) {
  98             printUsage();
  99             throw new Fault("Log file name is not given");
 100         }
 101 
 102         final String metaSize    = "-XX:MetaspaceSize=";
 103         final String maxMetaSize = "-XX:MaxMetaspaceSize=";
 104         final String minRatio    = "-XX:MinMetaspaceFreeRatio=";
 105         final String maxRatio    = "-XX:MaxMetaspaceFreeRatio=";
 106 
 107         for (String va: vmArgs) {
 108             if (va.startsWith(metaSize)) {
 109                 metaspaceSize = parseValue(va.substring(metaSize.length()));
 110             } else if (va.startsWith(maxMetaSize)) {
 111                 maxMetaspaceSize = parseValue(va.substring(maxMetaSize.length()));
 112             } else if (va.startsWith(minRatio)) {
 113                 minMetaspaceFreeRatio = parseValue(va.substring(minRatio.length()));
 114             } else if (va.startsWith(maxRatio)) {
 115                 maxMetaspaceFreeRatio = parseValue(va.substring(maxRatio.length()));
 116             }
 117         }
 118 
 119         if (metaspaceSize < 0) {
 120             printUsage();
 121             throw new Fault("-XX:MetaspaceSize is not specified");
 122         } else if (maxMetaspaceSize < 0) {
 123             printUsage();
 124             throw new Fault("-XX:MaxMetaspaceSize is not specified");
 125         } else if (minMetaspaceFreeRatio < 0) {
 126             printUsage();
 127             throw new Fault("-XX:MinMetaspaceFreeRatio is not specified");
 128         } else if (maxMetaspaceFreeRatio < 0) {
 129             printUsage();
 130             throw new Fault("-XX:MaxMetaspaceFreeRatio is not specified");
 131         }
 132 
 133     }
 134 
 135     private void printUsage() {
 136         System.err.println("Usage: ");
 137         System.err.println("java [-Xlog:gc:<filename>] [-XX:MetaspaceSize=..] [-XX:MaxMetaspaceSize=..] [-XX:MinMetaspaceFreeRatio=..] [-XX:MaxMetaspaceFreeRatio=..] \\");
 138         System.err.println("    " + HighWaterMarkTest.class.getCanonicalName());
 139     }
 140 
 141     /**
 142      * Check that MinMetaspaceFreeRatio/MaxMetaspaceFreeRatio settings
 143      * affects the moment of the next GC.
 144      *
 145      * Eats memory until amount of committed metaspace achieves a certain level
 146      * (between MetaspaceSize and MaxMetaspaceSize).
 147      * Then checks how many times GC has been invoked.
 148      *
 149      */
 150     @Override
 151     public void doCheck() {
 152 
 153         // to avoid timeouts we limit the number of attempts
 154         int attempts = 0;
 155         int maxAttempts = 10_000;
 156 
 157         // in between metaspaceSize and maxMetaspaceSize
 158         // no OOM is exepcted.
 159         long committedLevel = (metaspaceSize + maxMetaspaceSize) / 2;
 160 
 161         while (getCommitted() < committedLevel && attempts < maxAttempts) {
 162             attempts++;
 163             loadNewClasses(9, true);  // load classes and keep references
 164             loadNewClasses(1, false); // load classes without keeping references
 165         }
 166 
 167 
 168         System.out.println("% Classes loaded: " + attempts*10);
 169         System.out.println("% Used metaspace    : " + bytes2k(getUsed()));
 170         System.out.println("% Committed metaspce: " + bytes2k(getCommitted()));
 171 
 172         cleanLoadedClasses();
 173 
 174         if (attempts == maxAttempts) {
 175             throw new Fault("Committed amount hasn't achieved " + bytes2k(committedLevel));
 176         }
 177 
 178         if (VMRuntimeEnvUtils.isVMOptionEnabled("UseConcMarkSweepGC")) {
 179             System.out.println("ConcMarkSweep is used, cannot count GC");
 180             return;
 181         }
 182 
 183         int gcCount = getMetaspaceGCCount();
 184         if (gcCount < 0) {
 185             // perhpas, it's better to silently pass here... Let's see.
 186             throw new Fault ("Unable to count full collections, could be an env issue");
 187         }
 188         System.out.println("% GC has been invoked: " + gcCount + " times");
 189 
 190         if (maxMetaspaceFreeRatio <= 1) {
 191             // min/max = 0/1  boundary value
 192             // GC should happen very often
 193             checkGCCount(gcCount, 20, -1);
 194         } else if (minMetaspaceFreeRatio >= 99) {
 195             // min/max = 99/100  boundary value
 196             // GC should happen very rare
 197             checkGCCount(gcCount, -1, 2);
 198         } else if (minMetaspaceFreeRatio >= 10  && maxMetaspaceFreeRatio <= 20) {
 199             // GC should happen quite often
 200             checkGCCount(gcCount, 3, 30);
 201         } else if (minMetaspaceFreeRatio >= 70  && maxMetaspaceFreeRatio <= 80) {
 202             // GC should happen quite often
 203             checkGCCount(gcCount, 1, 3);
 204         } else {
 205             // hard to estimate
 206         }
 207 
 208     }
 209     /**
 210      * Checks that count of GC fits the expected range.
 211      * Throws Fault if count is unexpected.
 212      *
 213      * @param count how many times GC has happened
 214      * @param min   expected minimum, if under zero - undefined
 215      * @param max   expected maximum, if under zero - undefined
 216      */
 217     void checkGCCount(int count, int min, int max) {
 218         if (min < 0) {
 219             if(count > max) {
 220                 throw new Fault("GC has happened too often: " + count + " times, " +
 221                         "expected count: less than " + max);
 222             }
 223         } else if (max < 0) {
 224             if(count < min) {
 225                 throw new Fault("GC has happened too rare: " + count + " times, " +
 226                         "expected count greater than " + min);
 227             }
 228         } else if (count < min || count > max ) {
 229             throw new Fault ("GC has happened " + count + " times, " +
 230                     "approximate count is " + min + " to " + max);
 231         }
 232     }
 233 
 234 }