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