--- /dev/null 2018-05-02 09:10:36.097102948 -0700
+++ new/test/hotspot/jtreg/vmTestbase/metaspace/gc/HighWaterMarkTest.java 2018-05-08 10:20:13.233632176 -0700
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package metaspace.gc;
+
+import java.util.Arrays;
+import vm.share.VMRuntimeEnvUtils;
+
+/**
+ * Test metaspace ergonomic.
+ *
+ *
+ * - MetaspaceSize
+ *
- MaxMetaspaceSize
+ *
- MinMetaspaceFreeRatio
+ *
- MaxMetaspaceFreeRatio
+ *
+ *
+ * The test loads classes until the committed metaspace achieves the certain
+ * level between MetaspaceSize and MaxMetaspaceSize.
+ * Then it counts how many times GC has been induced.
+ * Test verifies that MinMetaspaceFreeRatio/MaxMetaspaceFreeRatio settings
+ * affect the frequency of GC. (High-water mark)
+ *
+ * Note: The test doesn't check the GC count if CMS is used.
+ *
+ * Quoting: Java SE 8 HotSpot[tm] Virtual Machine Garbage Collection Tuning
+ *
+ * Class metadata is deallocated when the corresponding Java class is unloaded.
+ * Java classes are unloaded as a results of garbage collection and garbage
+ * collections may be induced in order to unload classes and deallocate class
+ * metadata. When the space used for class metadata reaches a certain level
+ * (call it a high-water mark), a garbage collection is induced.
+ * After the garbage collection the high-water mark may be raised or lowered
+ * depending on the amount of space freed from class metadata. The high-water
+ * mark would be raised so as not to induce another garbage collection too soon.
+ * The high-water mark is initially set to the value of the command-line
+ * flag MetaspaceSize . It is raised or lowered based on the flags
+ * MaxMetaspaceFreeRatio and MinMetaspaceFreeRatio.
+ * If the committed space available for class metadata as a percentage of
+ * the total committed space for class metadata is greater than
+ * MaxMetaspaceFreeRatio, the high-water mark will be lowered.
+ * If it is less than MinMetaspaceFreeRatio, the high-water mark will be raised.
+ *
+ */
+public class HighWaterMarkTest extends FirstGCTest {
+
+ public static void main(String... args) {
+ new HighWaterMarkTest().run(args);
+ }
+
+ // value given in -XX:MetaspaceSize=
+ private long metaspaceSize = -1;
+
+ // value given in -XX:MaxMetaspaceSize=
+ private long maxMetaspaceSize = -1;
+
+ // value given in -XX:MinMetaspaceFreeRatio=
+ private long minMetaspaceFreeRatio = -1;
+
+ // value given in -XX:MaxMetaspaceFreeRatio=
+ private long maxMetaspaceFreeRatio = -1;
+
+ /**
+ * Parses arguments and vm options.
+ * Throws Fault in cases of wrong values or missed parameters.
+ *
+ * @param args command line options
+ */
+ @Override
+ protected void parseArgs(String[] args) {
+ if (args.length > 0) {
+ printUsage();
+ throw new Fault("Illegal arguments: " + Arrays.asList(args));
+ }
+
+ if (gclogFileName == null) {
+ printUsage();
+ throw new Fault("Log file name is not given");
+ }
+
+ final String metaSize = "-XX:MetaspaceSize=";
+ final String maxMetaSize = "-XX:MaxMetaspaceSize=";
+ final String minRatio = "-XX:MinMetaspaceFreeRatio=";
+ final String maxRatio = "-XX:MaxMetaspaceFreeRatio=";
+
+ for (String va: vmArgs) {
+ if (va.startsWith(metaSize)) {
+ metaspaceSize = parseValue(va.substring(metaSize.length()));
+ } else if (va.startsWith(maxMetaSize)) {
+ maxMetaspaceSize = parseValue(va.substring(maxMetaSize.length()));
+ } else if (va.startsWith(minRatio)) {
+ minMetaspaceFreeRatio = parseValue(va.substring(minRatio.length()));
+ } else if (va.startsWith(maxRatio)) {
+ maxMetaspaceFreeRatio = parseValue(va.substring(maxRatio.length()));
+ }
+ }
+
+ if (metaspaceSize < 0) {
+ printUsage();
+ throw new Fault("-XX:MetaspaceSize is not specified");
+ } else if (maxMetaspaceSize < 0) {
+ printUsage();
+ throw new Fault("-XX:MaxMetaspaceSize is not specified");
+ } else if (minMetaspaceFreeRatio < 0) {
+ printUsage();
+ throw new Fault("-XX:MinMetaspaceFreeRatio is not specified");
+ } else if (maxMetaspaceFreeRatio < 0) {
+ printUsage();
+ throw new Fault("-XX:MaxMetaspaceFreeRatio is not specified");
+ }
+
+ }
+
+ private void printUsage() {
+ System.err.println("Usage: ");
+ System.err.println("java [-Xlog:gc:] [-XX:MetaspaceSize=..] [-XX:MaxMetaspaceSize=..] [-XX:MinMetaspaceFreeRatio=..] [-XX:MaxMetaspaceFreeRatio=..] \\");
+ System.err.println(" " + HighWaterMarkTest.class.getCanonicalName());
+ }
+
+ /**
+ * Check that MinMetaspaceFreeRatio/MaxMetaspaceFreeRatio settings
+ * affects the moment of the next GC.
+ *
+ * Eats memory until amount of committed metaspace achieves a certain level
+ * (between MetaspaceSize and MaxMetaspaceSize).
+ * Then checks how many times GC has been invoked.
+ *
+ */
+ @Override
+ public void doCheck() {
+
+ // to avoid timeouts we limit the number of attempts
+ int attempts = 0;
+ int maxAttempts = 10_000;
+
+ // in between metaspaceSize and maxMetaspaceSize
+ // no OOM is exepcted.
+ long committedLevel = (metaspaceSize + maxMetaspaceSize) / 2;
+
+ while (getCommitted() < committedLevel && attempts < maxAttempts) {
+ attempts++;
+ loadNewClasses(9, true); // load classes and keep references
+ loadNewClasses(1, false); // load classes without keeping references
+ }
+
+
+ System.out.println("% Classes loaded: " + attempts*10);
+ System.out.println("% Used metaspace : " + bytes2k(getUsed()));
+ System.out.println("% Committed metaspce: " + bytes2k(getCommitted()));
+
+ cleanLoadedClasses();
+
+ if (attempts == maxAttempts) {
+ throw new Fault("Committed amount hasn't achieved " + bytes2k(committedLevel));
+ }
+
+ if (VMRuntimeEnvUtils.isVMOptionEnabled("UseConcMarkSweepGC")) {
+ System.out.println("ConcMarkSweep is used, cannot count GC");
+ return;
+ }
+
+ int gcCount = getMetaspaceGCCount();
+ if (gcCount < 0) {
+ // perhpas, it's better to silently pass here... Let's see.
+ throw new Fault ("Unable to count full collections, could be an env issue");
+ }
+ System.out.println("% GC has been invoked: " + gcCount + " times");
+
+ if (VMRuntimeEnvUtils.isVMOptionEnabled("UseG1GC") &&
+ VMRuntimeEnvUtils.isVMOptionEnabled("ClassUnloadingWithConcurrentMark")) {
+ System.out.println("% isG1ClassUnloading: true");
+ if (gcCount != 0) {
+ throw new Fault ("G1 should unload classes, full GC is not expected");
+ }
+ } else {
+ if (maxMetaspaceFreeRatio <= 1) {
+ // min/max = 0/1 boundary value
+ // GC should happen very often
+ checkGCCount(gcCount, 20, -1);
+ } else if (minMetaspaceFreeRatio >= 99) {
+ // min/max = 99/100 boundary value
+ // GC should happen very rare
+ checkGCCount(gcCount, -1, 2);
+ } else if (minMetaspaceFreeRatio >= 10 && maxMetaspaceFreeRatio <= 20) {
+ // GC should happen quite often
+ checkGCCount(gcCount, 3, 30);
+ } else if (minMetaspaceFreeRatio >= 70 && maxMetaspaceFreeRatio <= 80) {
+ // GC should happen quite often
+ checkGCCount(gcCount, 1, 3);
+ } else {
+ // hard to estimate
+ }
+ }
+
+ }
+ /**
+ * Checks that count of GC fits the expected range.
+ * Throws Fault if count is unexpected.
+ *
+ * @param count how many times GC has happened
+ * @param min expected minimum, if under zero - undefined
+ * @param max expected maximum, if under zero - undefined
+ */
+ void checkGCCount(int count, int min, int max) {
+ if (min < 0) {
+ if(count > max) {
+ throw new Fault("GC has happened too often: " + count + " times, " +
+ "expected count: less than " + max);
+ }
+ } else if (max < 0) {
+ if(count < min) {
+ throw new Fault("GC has happened too rare: " + count + " times, " +
+ "expected count greater than " + min);
+ }
+ } else if (count < min || count > max ) {
+ throw new Fault ("GC has happened " + count + " times, " +
+ "approximate count is " + min + " to " + max);
+ }
+ }
+
+}