1 /* 2 * Copyright (c) 2016, 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 package gc.g1.humongousObjects; 26 27 import gc.testlibrary.Helpers; 28 import jdk.test.lib.Asserts; 29 import sun.hotspot.WhiteBox; 30 31 import java.lang.management.GarbageCollectorMXBean; 32 import java.lang.management.ManagementFactory; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.List; 36 37 /** 38 * @test TestHeapCounters 39 * @summary Checks that heap counters work as expected after humongous allocations/deallocations 40 * @requires vm.gc.G1 41 * @library /test/lib / 42 * @modules java.base/jdk.internal.misc 43 * @modules java.management 44 * @build sun.hotspot.WhiteBox 45 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 46 * sun.hotspot.WhiteBox$WhiteBoxPermission 47 * 48 * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. 49 * -Xmx128m -Xms128m 50 * -XX:G1HeapRegionSize=1M -XX:InitiatingHeapOccupancyPercent=100 -XX:-G1UseAdaptiveIHOP 51 * -Xlog:gc -Xlog:gc:file=TestHeapCountersRuntime.gc.log 52 * gc.g1.humongousObjects.TestHeapCounters RUNTIME_COUNTER 53 * 54 * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. 55 * -Xmx128m -Xms128m 56 * -XX:G1HeapRegionSize=1M -XX:InitiatingHeapOccupancyPercent=100 -XX:-G1UseAdaptiveIHOP 57 * -Xlog:gc -Xlog:gc:file=TestHeapCountersMXBean.gc.log 58 * gc.g1.humongousObjects.TestHeapCounters MX_BEAN_COUNTER 59 */ 60 public class TestHeapCounters { 61 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 62 private static final int G1_REGION_SIZE = WHITE_BOX.g1RegionSize(); 63 private static final int HALF_G1_REGION_SIZE = G1_REGION_SIZE / 2; 64 65 // Since during deallocation GC could free (very unlikely) some non-humongous data this value relaxes amount of 66 // memory we expect to be freed. 67 private static final double ALLOCATION_SIZE_TOLERANCE_FACTOR = 0.85D; 68 69 private enum MemoryCounter { 70 MX_BEAN_COUNTER { 71 @Override 72 public long getUsedMemory() { 73 return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); 74 } 75 }, 76 RUNTIME_COUNTER { 77 @Override 78 public long getUsedMemory() { 79 return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); 80 } 81 }; 82 83 public abstract long getUsedMemory(); 84 } 85 86 private static class Allocation { 87 private byte[] allocation; 88 public final long expectedSize; 89 90 public Allocation(int allocationSize, long allocationExpectedSize) { 91 allocation = new byte[allocationSize]; 92 expectedSize = allocationExpectedSize; 93 94 System.out.println(String.format("Object size is %d; Object is %shumongous", 95 WHITE_BOX.getObjectSize(allocation), 96 (WHITE_BOX.g1IsHumongous(allocation) ? "" : "non-"))); 97 98 selfTest(); 99 } 100 101 private void selfTest() { 102 boolean isHumongous = WHITE_BOX.getObjectSize(allocation) > HALF_G1_REGION_SIZE; 103 boolean shouldBeHumongous = WHITE_BOX.g1IsHumongous(allocation); 104 105 // Sanity check 106 Asserts.assertEquals(isHumongous, shouldBeHumongous, 107 String.format("Test Bug: Object of size %d is expected to be %shumongous but it is not", 108 WHITE_BOX.getObjectSize(allocation), (shouldBeHumongous ? "" : "non-"))); 109 } 110 111 public void forgetAllocation() { 112 allocation = null; 113 } 114 } 115 116 public static void main(String[] args) { 117 118 if (args.length != 1) { 119 throw new Error("Expected memory counter name wasn't provided as command line argument"); 120 } 121 MemoryCounter memoryCounter = MemoryCounter.valueOf(args[0].toUpperCase()); 122 123 int byteArrayMemoryOverhead = Helpers.detectByteArrayAllocationOverhead(); 124 125 // Largest non-humongous byte[] 126 int maxByteArrayNonHumongousSize = HALF_G1_REGION_SIZE - byteArrayMemoryOverhead; 127 128 // Maximum byte[] that takes one region 129 int maxByteArrayOneRegionSize = G1_REGION_SIZE - byteArrayMemoryOverhead; 130 131 List<Integer> allocationSizes = Arrays.asList( 132 (int) maxByteArrayNonHumongousSize + 1, 133 (int) (0.8f * maxByteArrayOneRegionSize), 134 (int) (maxByteArrayOneRegionSize), 135 (int) (1.2f * maxByteArrayOneRegionSize), 136 (int) (1.5f * maxByteArrayOneRegionSize), 137 (int) (1.7f * maxByteArrayOneRegionSize), 138 (int) (2.0f * maxByteArrayOneRegionSize), 139 (int) (2.5f * maxByteArrayOneRegionSize) 140 ); 141 142 List<Allocation> allocations = new ArrayList<>(); 143 List<GarbageCollectorMXBean> gcBeans = 144 ManagementFactory.getGarbageCollectorMXBeans(); 145 146 long gcCountBefore = gcBeans.stream().mapToLong(GarbageCollectorMXBean::getCollectionCount).sum(); 147 148 149 System.out.println("Starting allocations - no GC should happen until we finish them"); 150 151 for (int allocationSize : allocationSizes) { 152 153 long usedMemoryBefore = memoryCounter.getUsedMemory(); 154 long expectedAllocationSize = (long) Math.ceil((double) allocationSize / G1_REGION_SIZE) * G1_REGION_SIZE; 155 allocations.add(new Allocation(allocationSize, expectedAllocationSize)); 156 long usedMemoryAfter = memoryCounter.getUsedMemory(); 157 158 System.out.format("Expected allocation size: %d\nUsed memory before allocation: %d\n" 159 + "Used memory after allocation: %d\n", 160 expectedAllocationSize, usedMemoryBefore, usedMemoryAfter); 161 162 long gcCountNow = gcBeans.stream().mapToLong(GarbageCollectorMXBean::getCollectionCount).sum(); 163 164 if (gcCountNow == gcCountBefore) { 165 // We should allocate at least allocation.expectedSize 166 Asserts.assertGreaterThanOrEqual(usedMemoryAfter - usedMemoryBefore, expectedAllocationSize, 167 "Counter of type " + memoryCounter.getClass().getSimpleName() + 168 " returned wrong allocation size"); 169 } else { 170 System.out.println("GC happened during allocation so the check is skipped"); 171 gcCountBefore = gcCountNow; 172 } 173 } 174 175 System.out.println("Finished allocations - no GC should have happened before this line"); 176 177 178 allocations.stream().forEach(allocation -> { 179 long usedMemoryBefore = memoryCounter.getUsedMemory(); 180 allocation.forgetAllocation(); 181 182 WHITE_BOX.fullGC(); 183 184 long usedMemoryAfter = memoryCounter.getUsedMemory(); 185 186 // We should free at least allocation.expectedSize * ALLOCATION_SIZE_TOLERANCE_FACTOR 187 Asserts.assertGreaterThanOrEqual(usedMemoryBefore - usedMemoryAfter, 188 (long) (allocation.expectedSize * ALLOCATION_SIZE_TOLERANCE_FACTOR), 189 "Counter of type " + memoryCounter.getClass().getSimpleName() + " returned wrong allocation size"); 190 }); 191 } 192 }