8132721: Add tests which check that heap counters work as expected during Humongous allocations
Reviewed-by: jmasa, tschatzl, dfazunen
This commit is contained in:
parent
00aa34d788
commit
5c9133fb92
193
hotspot/test/gc/g1/humongousObjects/TestHeapCounters.java
Normal file
193
hotspot/test/gc/g1/humongousObjects/TestHeapCounters.java
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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 gc.g1.humongousObjects;
|
||||
|
||||
import gc.testlibrary.Helpers;
|
||||
import jdk.test.lib.Asserts;
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
import java.lang.management.GarbageCollectorMXBean;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @test TestHeapCounters
|
||||
* @summary Checks that heap counters work as expected after humongous allocations/deallocations
|
||||
* @requires vm.gc=="G1" | vm.gc=="null"
|
||||
* @library /testlibrary /test/lib /
|
||||
* @modules java.management
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* gc.testlibrary.Helpers
|
||||
* gc.g1.humongousObjects.TestHeapCounters
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
*
|
||||
* @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
|
||||
* -Xmx128m -Xms128m
|
||||
* -XX:G1HeapRegionSize=1M -XX:InitiatingHeapOccupancyPercent=100 -XX:-G1UseAdaptiveIHOP
|
||||
* -Xlog:gc -Xlog:gc:file=TestHeapCountersRuntime.gc.log
|
||||
* gc.g1.humongousObjects.TestHeapCounters RUNTIME_COUNTER
|
||||
*
|
||||
* @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
|
||||
* -Xmx128m -Xms128m
|
||||
* -XX:G1HeapRegionSize=1M -XX:InitiatingHeapOccupancyPercent=100 -XX:-G1UseAdaptiveIHOP
|
||||
* -Xlog:gc -Xlog:gc:file=TestHeapCountersMXBean.gc.log
|
||||
* gc.g1.humongousObjects.TestHeapCounters MX_BEAN_COUNTER
|
||||
*/
|
||||
public class TestHeapCounters {
|
||||
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
|
||||
private static final int G1_REGION_SIZE = WHITE_BOX.g1RegionSize();
|
||||
private static final int HALF_G1_REGION_SIZE = G1_REGION_SIZE / 2;
|
||||
|
||||
// Since during deallocation GC could free (very unlikely) some non-humongous data this value relaxes amount of
|
||||
// memory we expect to be freed.
|
||||
private static final double ALLOCATION_SIZE_TOLERANCE_FACTOR = 0.85D;
|
||||
|
||||
private enum MemoryCounter {
|
||||
MX_BEAN_COUNTER {
|
||||
@Override
|
||||
public long getUsedMemory() {
|
||||
return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
|
||||
}
|
||||
},
|
||||
RUNTIME_COUNTER {
|
||||
@Override
|
||||
public long getUsedMemory() {
|
||||
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
|
||||
}
|
||||
};
|
||||
|
||||
public abstract long getUsedMemory();
|
||||
}
|
||||
|
||||
private static class Allocation {
|
||||
private byte[] allocation;
|
||||
public final long expectedSize;
|
||||
|
||||
public Allocation(int allocationSize, long allocationExpectedSize) {
|
||||
allocation = new byte[allocationSize];
|
||||
expectedSize = allocationExpectedSize;
|
||||
|
||||
System.out.println(String.format("Object size is %d; Object is %shumongous",
|
||||
WHITE_BOX.getObjectSize(allocation),
|
||||
(WHITE_BOX.g1IsHumongous(allocation) ? "" : "non-")));
|
||||
|
||||
selfTest();
|
||||
}
|
||||
|
||||
private void selfTest() {
|
||||
boolean isHumongous = WHITE_BOX.getObjectSize(allocation) > HALF_G1_REGION_SIZE;
|
||||
boolean shouldBeHumongous = WHITE_BOX.g1IsHumongous(allocation);
|
||||
|
||||
// Sanity check
|
||||
Asserts.assertEquals(isHumongous, shouldBeHumongous,
|
||||
String.format("Test Bug: Object of size %d is expected to be %shumongous but it is not",
|
||||
WHITE_BOX.getObjectSize(allocation), (shouldBeHumongous ? "" : "non-")));
|
||||
}
|
||||
|
||||
public void forgetAllocation() {
|
||||
allocation = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
if (args.length != 1) {
|
||||
throw new Error("Expected memory counter name wasn't provided as command line argument");
|
||||
}
|
||||
MemoryCounter memoryCounter = MemoryCounter.valueOf(args[0].toUpperCase());
|
||||
|
||||
int byteArrayMemoryOverhead = Helpers.detectByteArrayAllocationOverhead();
|
||||
|
||||
// Largest non-humongous byte[]
|
||||
int maxByteArrayNonHumongousSize = HALF_G1_REGION_SIZE - byteArrayMemoryOverhead;
|
||||
|
||||
// Maximum byte[] that takes one region
|
||||
int maxByteArrayOneRegionSize = G1_REGION_SIZE - byteArrayMemoryOverhead;
|
||||
|
||||
List<Integer> allocationSizes = Arrays.asList(
|
||||
(int) maxByteArrayNonHumongousSize + 1,
|
||||
(int) (0.8f * maxByteArrayOneRegionSize),
|
||||
(int) (maxByteArrayOneRegionSize),
|
||||
(int) (1.2f * maxByteArrayOneRegionSize),
|
||||
(int) (1.5f * maxByteArrayOneRegionSize),
|
||||
(int) (1.7f * maxByteArrayOneRegionSize),
|
||||
(int) (2.0f * maxByteArrayOneRegionSize),
|
||||
(int) (2.5f * maxByteArrayOneRegionSize)
|
||||
);
|
||||
|
||||
List<Allocation> allocations = new ArrayList<>();
|
||||
List<GarbageCollectorMXBean> gcBeans =
|
||||
ManagementFactory.getGarbageCollectorMXBeans();
|
||||
|
||||
long gcCountBefore = gcBeans.stream().mapToLong(GarbageCollectorMXBean::getCollectionCount).sum();
|
||||
|
||||
|
||||
System.out.println("Starting allocations - no GC should happen until we finish them");
|
||||
|
||||
for (int allocationSize : allocationSizes) {
|
||||
|
||||
long usedMemoryBefore = memoryCounter.getUsedMemory();
|
||||
long expectedAllocationSize = (long) Math.ceil((double) allocationSize / G1_REGION_SIZE) * G1_REGION_SIZE;
|
||||
allocations.add(new Allocation(allocationSize, expectedAllocationSize));
|
||||
long usedMemoryAfter = memoryCounter.getUsedMemory();
|
||||
|
||||
System.out.format("Expected allocation size: %d\nUsed memory before allocation: %d\n"
|
||||
+ "Used memory after allocation: %d\n",
|
||||
expectedAllocationSize, usedMemoryBefore, usedMemoryAfter);
|
||||
|
||||
long gcCountNow = gcBeans.stream().mapToLong(GarbageCollectorMXBean::getCollectionCount).sum();
|
||||
|
||||
if (gcCountNow == gcCountBefore) {
|
||||
// We should allocate at least allocation.expectedSize
|
||||
Asserts.assertGreaterThanOrEqual(usedMemoryAfter - usedMemoryBefore, expectedAllocationSize,
|
||||
"Counter of type " + memoryCounter.getClass().getSimpleName() +
|
||||
" returned wrong allocation size");
|
||||
} else {
|
||||
System.out.println("GC happened during allocation so the check is skipped");
|
||||
gcCountBefore = gcCountNow;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Finished allocations - no GC should have happened before this line");
|
||||
|
||||
|
||||
allocations.stream().forEach(allocation -> {
|
||||
long usedMemoryBefore = memoryCounter.getUsedMemory();
|
||||
allocation.forgetAllocation();
|
||||
|
||||
WHITE_BOX.fullGC();
|
||||
|
||||
long usedMemoryAfter = memoryCounter.getUsedMemory();
|
||||
|
||||
// We should free at least allocation.expectedSize * ALLOCATION_SIZE_TOLERANCE_FACTOR
|
||||
Asserts.assertGreaterThanOrEqual(usedMemoryBefore - usedMemoryAfter,
|
||||
(long) (allocation.expectedSize * ALLOCATION_SIZE_TOLERANCE_FACTOR),
|
||||
"Counter of type " + memoryCounter.getClass().getSimpleName() + " returned wrong allocation size");
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user