8037925: CMM Testing: an allocated humongous object at the end of the heap should not prevents shrinking the heap
authorjwilhelm
Tue, 15 Apr 2014 18:09:53 +0200
changeset 24097 99374b567304
parent 24096 6f7e28b293fb
child 24098 48f07e2c74de
8037925: CMM Testing: an allocated humongous object at the end of the heap should not prevents shrinking the heap Summary: New test added. Reviewed-by: ehelin, tschatzl, jwilhelm Contributed-by: andrey.x.zakharov@oracle.com
hotspot/test/TEST.groups
hotspot/test/gc/g1/TestHumongousShrinkHeap.java
--- a/hotspot/test/TEST.groups	Mon Apr 14 10:08:10 2014 +0200
+++ b/hotspot/test/TEST.groups	Tue Apr 15 18:09:53 2014 +0200
@@ -127,6 +127,7 @@
   gc/6581734/Test6581734.java \
   gc/7072527/TestFullGCCount.java \
   gc/g1/TestHumongousAllocInitialMark.java \
+  gc/g1/TestHumongousShrinkHeap.java \
   gc/arguments/TestG1HeapRegionSize.java \
   gc/metaspace/TestMetaspaceMemoryPool.java \
   gc/arguments/TestDynMinHeapFreeRatio.java \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/TestHumongousShrinkHeap.java	Tue Apr 15 18:09:53 2014 +0200
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+/**
+ * @test TestHumongousShrinkHeap
+ * @bug 8036025
+ * @summary Verify that heap shrinks after GC in the presence of fragmentation due to humongous objects
+ * @library /testlibrary
+ * @run main/othervm -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=50 -XX:+UseG1GC -XX:G1HeapRegionSize=1M -verbose:gc TestHumongousShrinkHeap
+ */
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryUsage;
+import java.util.ArrayList;
+import java.util.List;
+import sun.management.ManagementFactoryHelper;
+import static com.oracle.java.testlibrary.Asserts.*;
+
+public class TestHumongousShrinkHeap {
+
+    public static final String MIN_FREE_RATIO_FLAG_NAME = "MinHeapFreeRatio";
+    public static final String MAX_FREE_RATIO_FLAG_NAME = "MaxHeapFreeRatio";
+
+    private static final ArrayList<ArrayList<byte[]>> garbage = new ArrayList<>();
+    private static final int PAGE_SIZE = 1024 * 1024; // 1M
+    private static final int PAGES_NUM = 5;
+
+
+    public static void main(String[] args) {
+        new TestHumongousShrinkHeap().test();
+    }
+
+    private final void test() {
+        System.gc();
+        MemoryUsagePrinter.printMemoryUsage("init");
+
+        eat();
+        MemoryUsagePrinter.printMemoryUsage("eaten");
+        MemoryUsage muFull = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
+
+        free();
+        MemoryUsagePrinter.printMemoryUsage("free");
+        MemoryUsage muFree = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
+
+        assertLessThan(muFree.getCommitted(), muFull.getCommitted(), String.format(
+                "committed free heap size is not less than committed full heap size, heap hasn't been shrunk?%n"
+                + "%s = %s%n%s = %s",
+                MIN_FREE_RATIO_FLAG_NAME,
+                ManagementFactoryHelper.getDiagnosticMXBean().getVMOption(MIN_FREE_RATIO_FLAG_NAME).getValue(),
+                MAX_FREE_RATIO_FLAG_NAME,
+                ManagementFactoryHelper.getDiagnosticMXBean().getVMOption(MAX_FREE_RATIO_FLAG_NAME).getValue()
+        ));
+    }
+
+    private void eat() {
+        int HumongousObjectSize = Math.round(.9f * PAGE_SIZE);
+        System.out.println("Will allocate objects of size=" +
+                MemoryUsagePrinter.humanReadableByteCount(HumongousObjectSize, true));
+
+        for (int i = 0; i < PAGES_NUM; i++) {
+            ArrayList<byte[]> stuff = new ArrayList<>();
+            eatList(stuff, 100, HumongousObjectSize);
+            MemoryUsagePrinter.printMemoryUsage("eat #" + i);
+            garbage.add(stuff);
+        }
+    }
+
+    private void free() {
+        // do not free last one list
+        garbage.subList(0, garbage.size() - 1).clear();
+
+        // do not free last one element from last list
+        ArrayList stuff = garbage.get(garbage.size() - 1);
+        stuff.subList(0, stuff.size() - 1).clear();
+        System.gc();
+    }
+
+    private static void eatList(List garbage, int count, int size) {
+        for (int i = 0; i < count; i++) {
+            garbage.add(new byte[size]);
+        }
+    }
+}
+
+/**
+ * Prints memory usage to standard output
+ */
+class MemoryUsagePrinter {
+
+    public static String humanReadableByteCount(long bytes, boolean si) {
+        int unit = si ? 1000 : 1024;
+        if (bytes < unit) {
+            return bytes + " B";
+        }
+        int exp = (int) (Math.log(bytes) / Math.log(unit));
+        String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
+        return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
+    }
+
+    public static void printMemoryUsage(String label) {
+        MemoryUsage memusage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
+        float freeratio = 1f - (float) memusage.getUsed() / memusage.getCommitted();
+        System.out.format("[%-24s] init: %-7s, used: %-7s, comm: %-7s, freeRatio ~= %.1f%%%n",
+                label,
+                humanReadableByteCount(memusage.getInit(), true),
+                humanReadableByteCount(memusage.getUsed(), true),
+                humanReadableByteCount(memusage.getCommitted(), true),
+                freeratio * 100
+        );
+    }
+}