/* * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import java.util.regex.Pattern; import jrockit.Asserts; import jrockit.jfr.TestRecording; import oracle.jrockit.jfr.parser.FLREvent; import static jrockit.Asserts.*; /* * @test * @key jfr * @library ../common * @build jrockit.jfr.ParseHelper * @run main/othervm -XX:G1HeapRegionSize=1m -Xmx64m -Xmn16m -XX:+UnlockCommercialFeatures -XX:+UseG1GC -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps TestEvacuationInfoEvent */ public class TestEvacuationInfoEvent { private final static String EVENT_INFO_PATH = "vm/gc/detailed/evacuation_info"; private final static String EVENT_FAILED_PATH = "vm/gc/detailed/evacuation_failed"; public static List dummyList; public static void main(String[] args) throws Throwable { TestRecording r = new TestRecording(); final long g1HeapRegionSize = 1024 * 1024; try { r.createJVMSetting(EVENT_INFO_PATH, true, false, 0, 0); r.createJVMSetting(EVENT_FAILED_PATH, true, false, 0, 0); r.start(); allocate(); r.stop(); String regexpPattern = String.format("(%s)|(%s)", EVENT_INFO_PATH, EVENT_FAILED_PATH); List all = r.parser().findJVMEvents(Pattern.compile(regexpPattern)); Asserts.assertFalse(all.isEmpty(), String.format("No events of type '%s' found", EVENT_INFO_PATH)); // Save gcIds for all EvacuationFailedEvents. These will be used to verify the EvacuationInfo event. Set failedEvacuationGcIds = new HashSet(); for (FLREvent event : all) { if (EVENT_FAILED_PATH.equals(event.getPath())) { failedEvacuationGcIds.add((Integer)event.getValue("gcId")); } } for (FLREvent event : all) { if (EVENT_INFO_PATH.equals(event.getPath())) { int setRegions = ((Integer)event.getValue("cSetRegions")).intValue(); long setUsedBefore = ((Long)event.getValue("cSetUsedBefore")).longValue(); long setUsedAfter = ((Long)event.getValue("cSetUsedAfter")).longValue(); int allocationRegions = ((Integer)event.getValue("allocationRegions")).intValue(); long allocRegionsUsedBefore = ((Long)event.getValue("allocRegionsUsedBefore")).longValue(); long allocRegionsUsedAfter = ((Long)event.getValue("allocRegionsUsedAfter")).longValue(); long bytesCopied = ((Long)event.getValue("bytesCopied")).longValue(); int regionsFreed = ((Integer)event.getValue("regionsFreed")).intValue(); try { assertGreaterOrEqualThan(setRegions, 0, "setRegions >= 0"); assertGreaterOrEqualThan(setUsedBefore, 0L, "setUsedBefore>= 0"); assertGreaterOrEqualThan(setUsedAfter, 0L, "setUsedAfter >= 0"); assertGreaterOrEqualThan(allocationRegions, 0, "allocationRegions >= 0"); assertGreaterOrEqualThan(allocRegionsUsedBefore, 0L, "allocRegionsUsedBefore >= 0"); assertGreaterOrEqualThan(allocRegionsUsedAfter, 0L, "allocRegionsUsedAfter >= 0"); assertGreaterOrEqualThan(bytesCopied, 0L, "bytesCopied >= 0"); assertGreaterOrEqualThan(regionsFreed, 0, "regionsFreed >= 0"); assertGreaterOrEqualThan(setUsedBefore, setUsedAfter, "setUsedBefore >= setUsedAfter"); assertEquals(allocRegionsUsedBefore + bytesCopied, allocRegionsUsedAfter, "allocRegionsUsedBefore + bytesCopied = allocRegionsUsedAfter"); assertGreaterOrEqualThan(setRegions, regionsFreed, "setRegions >= regionsFreed"); assertGreaterOrEqualThan(g1HeapRegionSize * allocationRegions, allocRegionsUsedAfter, "G1HeapRegionSize * allocationRegions >= allocationRegionsUsedAfter"); assertGreaterOrEqualThan(g1HeapRegionSize * setRegions, setUsedAfter, "G1HeapRegionSize * setRegions >= setUsedAfter"); assertGreaterOrEqualThan(g1HeapRegionSize * setRegions, setUsedBefore, "G1HeapRegionSize * setRegions >= setUsedBefore"); assertGreaterOrEqualThan(g1HeapRegionSize, allocRegionsUsedBefore, "G1HeapRegionSize >= allocRegionsUsedBefore"); if (failedEvacuationGcIds.contains((Integer)event.getValue("gcId"))) { assertGreaterThan(setUsedAfter, 0L, "EvacuationFailure -> setUsedAfter > 0"); assertGreaterThan(setRegions, regionsFreed, "EvacuationFailure -> setRegions > regionsFreed"); } else { assertEquals(setUsedAfter, 0L, "No EvacuationFailure -> setUsedAfter = 0"); assertEquals(setRegions, regionsFreed, "No EvacuationFailure -> setRegions = regionsFreed"); } } catch (Throwable t) { System.out.println("Failed event:"); System.out.println(event.toString()); throw t; } } } } catch (Throwable e) { e.printStackTrace(); r.copyTo("TestEvacuationInfoEvent_failed.jfr"); throw e; } finally { r.close(); } } /** * Allocate memory to trigger garbage collections. * We want the allocated objects to have different life time, because we want both "young" and "old" objects. * This is done by keeping the objects in an array and step the current index by a small random number in the loop. * The loop will continue until we have allocated a fixed number of bytes. */ private static void allocate() { Random r = new Random(0); final int arraySize = 6000; dummyList = new ArrayList(arraySize); for (int c = 0; c < arraySize; c++) { dummyList.add(null); } long bytesToAllocate = 256 * 1024 * 1024; int currPos = 0; while (bytesToAllocate > 0) { try { int nextAlloc = 1000 + (r.nextInt(4000)); bytesToAllocate -= nextAlloc; dummyList.set(currPos, new DummyObject(nextAlloc)); // Skip a few positions to get different duration on the objects. currPos += r.nextInt(20); if (currPos >= arraySize) { currPos = 0; } } catch (OutOfMemoryError e) { // We should not get an OOM, but if we do, just clear the dummyList. dummyList = null; System.gc(); dummyList = new ArrayList(arraySize); for (int c = 0; c < arraySize; c++) { dummyList.add(null); } } } dummyList = null; System.gc(); } private static class DummyObject { public byte[] payload; DummyObject(int size) { payload = new byte[size]; } } }