1 /*
  2  * Copyright (c) 2014, 2019, 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 gc.g1;
 25 
 26 /**
 27  * @test TestHumongousShrinkHeap
 28  * @bug 8036025 8056043
 29  * @requires vm.gc.G1
 30  * @summary Verify that heap shrinks after GC in the presence of fragmentation
 31  * due to humongous objects
 32  * @library /test/lib /
 33  * @modules java.base/jdk.internal.misc
 34  * @modules java.management/sun.management
 35  * @run main/othervm -XX:-ExplicitGCInvokesConcurrent -XX:MinHeapFreeRatio=10
 36  * -XX:MaxHeapFreeRatio=12 -XX:+UseG1GC -XX:G1HeapRegionSize=1M -verbose:gc
 37  * gc.g1.TestHumongousShrinkHeap
 38  */
 39 
 40 import com.sun.management.HotSpotDiagnosticMXBean;
 41 import java.lang.management.ManagementFactory;
 42 import java.lang.management.MemoryUsage;
 43 import java.util.ArrayList;
 44 import java.util.List;
 45 import java.text.NumberFormat;
 46 import gc.testlibrary.Helpers;
 47 import static jdk.test.lib.Asserts.*;
 48 import jtreg.SkippedException;
 49 
 50 public class TestHumongousShrinkHeap {
 51 
 52     public static final String MIN_FREE_RATIO_FLAG_NAME = "MinHeapFreeRatio";
 53     public static final String MAX_FREE_RATIO_FLAG_NAME = "MaxHeapFreeRatio";
 54 
 55     private static final List<List<byte[]>> garbage = new ArrayList();
 56     private static final int REGION_SIZE = 1024 * 1024; // 1M
 57     private static final int LISTS_COUNT = 10;
 58     private static final int HUMON_SIZE = Math.round(.9f * REGION_SIZE);
 59 
 60     private static final long TOTAL_MEMORY = Runtime.getRuntime().totalMemory();
 61     private static final long MAX_MEMORY = Runtime.getRuntime().maxMemory();
 62 
 63     private static final int HUMON_COUNT = (int) ((TOTAL_MEMORY / HUMON_SIZE) / LISTS_COUNT);
 64 
 65     public static void main(String[] args) {
 66         if (HUMON_COUNT == 0) {
 67             throw new SkippedException("Heap is too small");
 68         }
 69 
 70         if (TOTAL_MEMORY + REGION_SIZE * HUMON_COUNT > MAX_MEMORY) {
 71             throw new SkippedException("Initial heap size is to close to max heap size.");
 72         }
 73 
 74         System.out.format("Running with %s initial heap size of %s maximum heap size. "
 75                           + "Will allocate humongous object of %s size %d times.%n",
 76                           MemoryUsagePrinter.NF.format(TOTAL_MEMORY),
 77                           MemoryUsagePrinter.NF.format(MAX_MEMORY),
 78                           MemoryUsagePrinter.NF.format(HUMON_SIZE),
 79                           HUMON_COUNT
 80         );
 81         new TestHumongousShrinkHeap().test();
 82     }
 83 
 84     private final void test() {
 85         System.gc();
 86         MemoryUsagePrinter.printMemoryUsage("init");
 87 
 88         allocate();
 89         MemoryUsagePrinter.printMemoryUsage("allocated");
 90         MemoryUsage muFull = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
 91 
 92         free();
 93         MemoryUsagePrinter.printMemoryUsage("free");
 94         MemoryUsage muFree = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
 95 
 96         assertLessThan(muFree.getCommitted(), muFull.getCommitted(), String.format(
 97                 "committed free heap size is not less than committed full heap size, heap hasn't been shrunk?%n"
 98                 + "%s = %s%n%s = %s",
 99                 MIN_FREE_RATIO_FLAG_NAME,
100                 ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class)
101                     .getVMOption(MIN_FREE_RATIO_FLAG_NAME).getValue(),
102                 MAX_FREE_RATIO_FLAG_NAME,
103                 ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class)
104                     .getVMOption(MAX_FREE_RATIO_FLAG_NAME).getValue()
105         ));
106     }
107 
108     private void allocate() {
109 
110         for (int i = 0; i < LISTS_COUNT; i++) {
111             List<byte[]> stuff = new ArrayList();
112             allocateList(stuff, HUMON_COUNT, HUMON_SIZE);
113             MemoryUsagePrinter.printMemoryUsage("allocate #" + (i+1));
114             garbage.add(stuff);
115         }
116     }
117 
118     private void free() {
119         // do not free last one list
120         garbage.subList(0, garbage.size() - 1).clear();
121 
122         // do not free last one element from last list
123         List stuff = garbage.get(garbage.size() - 1);
124         stuff.subList(0, stuff.size() - 1).clear();
125         System.gc();
126     }
127 
128     private static void allocateList(List garbage, int count, int size) {
129         for (int i = 0; i < count; i++) {
130             garbage.add(new byte[size]);
131         }
132     }
133 }
134 
135 /**
136  * Prints memory usage to standard output
137  */
138 class MemoryUsagePrinter {
139 
140     public static final NumberFormat NF = Helpers.numberFormatter();
141 
142     public static void printMemoryUsage(String label) {
143         MemoryUsage memusage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
144         float freeratio = 1f - (float) memusage.getUsed() / memusage.getCommitted();
145         System.out.format("[%-24s] init: %-7s, used: %-7s, comm: %-7s, freeRatio ~= %.1f%%%n",
146                 label,
147                 NF.format(memusage.getInit()),
148                 NF.format(memusage.getUsed()),
149                 NF.format(memusage.getCommitted()),
150                 freeratio * 100
151         );
152     }
153 }