diff --git a/src/share/classes/jdk/jfr/EventNames.java b/src/share/classes/jdk/jfr/EventNames.java new file mode 100644 index 0000000000000000000000000000000000000000..8f1ac349aa0219acc1edc08e94b3eff2922db8b9 --- /dev/null +++ b/src/share/classes/jdk/jfr/EventNames.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. 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. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + * + */ +package jdk.jfr; + +/** + * A constant class for JFR Event, contains the names of some events used by jdk. + */ +public class EventNames { + private final static String PREFIX = "jdk."; + + public final static String OptoInstanceObjectAllocation = PREFIX + "OptoInstanceObjectAllocation"; + public final static String OptoArrayObjectAllocation = PREFIX + "OptoArrayObjectAllocation"; +} diff --git a/src/share/classes/jdk/jfr/Recording.java b/src/share/classes/jdk/jfr/Recording.java index 43d54a39958d320ef0e8eeb56a79ab3f83ce4267..c36ccc055c44e20c4c252b84411f7349a1d492ba 100644 --- a/src/share/classes/jdk/jfr/Recording.java +++ b/src/share/classes/jdk/jfr/Recording.java @@ -673,4 +673,7 @@ public final class Recording implements Closeable { internal.setSetting(id, value); } + public boolean isRecorderEnabled(String eventName) { + return internal.isRecorderEnabled(eventName); + } } diff --git a/src/share/classes/jdk/jfr/conf/default.jfc b/src/share/classes/jdk/jfr/conf/default.jfc index f759cd02d1e55e20e4ed5594686712afcaef759a..ce9050999ad9f575cb582f03d4b321859529cfbe 100644 --- a/src/share/classes/jdk/jfr/conf/default.jfc +++ b/src/share/classes/jdk/jfr/conf/default.jfc @@ -629,6 +629,16 @@ 10 ms + + false + true + + + + false + true + + diff --git a/src/share/classes/jdk/jfr/conf/profile.jfc b/src/share/classes/jdk/jfr/conf/profile.jfc index a8e168bbeb2790ddeb7baa3e7d3b5c7c8ee61e69..3d7e170b77fdf8c14798bb4c9b12fe386f75e7ba 100644 --- a/src/share/classes/jdk/jfr/conf/profile.jfc +++ b/src/share/classes/jdk/jfr/conf/profile.jfc @@ -629,6 +629,16 @@ 10 ms + + false + true + + + + false + true + + diff --git a/src/share/classes/jdk/jfr/consumer/RecordedClass.java b/src/share/classes/jdk/jfr/consumer/RecordedClass.java index 66bca255411d1ffbf8034b94f52b3b021d23ec9f..57263cd5342876bf41692b7b41eb59620ac70f44 100644 --- a/src/share/classes/jdk/jfr/consumer/RecordedClass.java +++ b/src/share/classes/jdk/jfr/consumer/RecordedClass.java @@ -100,4 +100,27 @@ public final class RecordedClass extends RecordedObject { public long getId() { return uniqueId; } + + /** + * Returns the object size for the class. + *

+ * The object size for instance class is accurate. But for the array class, it + * is a magic code 0x1111baba. The array object size can not be determined statically + * from the JVM klass information because array object size is affected by + * actual element length. + * + * @return the object size (instance object), or magic code 0x1111baba (array object) + */ + public int getObjectSize() { + return getTyped("objectSize", Integer.class, -1); + } + + /** + * Checks whether the class is for instance or array. + * + * @return true or false + */ + public boolean isArray() { + return getName().startsWith("["); + } } diff --git a/src/share/classes/jdk/jfr/internal/JVM.java b/src/share/classes/jdk/jfr/internal/JVM.java index bd7d086c47d0b1d805c30307638a165cd545fe5e..f8067f4e39edc9bcd8b9478268bca3ac7a6279b5 100644 --- a/src/share/classes/jdk/jfr/internal/JVM.java +++ b/src/share/classes/jdk/jfr/internal/JVM.java @@ -293,6 +293,20 @@ public final class JVM { */ public native void setSampleThreads(boolean sampleThreads) throws IllegalStateException; + /** + * Turn on/off objects allocations sampling. + * + * @param sampleAllocations true if object allocations should be sampled, false otherwise. + */ + public native void setSampleObjectAllocations(boolean sampleAllocations); + + /** + * Set object allocations sampling interval. + * + * @param interval + */ + public native void setObjectAllocationsSamplingInterval(long interval) throws IllegalArgumentException; + /** * Turn on/off compressed integers. * diff --git a/src/share/classes/jdk/jfr/internal/Options.java b/src/share/classes/jdk/jfr/internal/Options.java index 23f3919bb3d14fd2b899cd5efaf0807e4421232b..ed6a15d49504c3b33b740838e548fe551421ca37 100644 --- a/src/share/classes/jdk/jfr/internal/Options.java +++ b/src/share/classes/jdk/jfr/internal/Options.java @@ -49,6 +49,8 @@ public final class Options { private static final boolean DEFAULT_SAMPLE_THREADS = true; private static final long DEFAULT_MAX_CHUNK_SIZE = 12 * 1024 * 1024; private static final SafePath DEFAULT_DUMP_PATH = SecuritySupport.USER_HOME; + private static final boolean DEFAULT_SAMPLE_OBJECT_ALLOCATIONS = false; + private static final long DEFAULT_OBJECT_ALLOCATIONS_SAMPLING_INTERVAL = 1024; private static long memorySize; private static long globalBufferSize; @@ -58,6 +60,8 @@ public final class Options { private static boolean sampleThreads; private static long maxChunkSize; private static SafePath dumpPath; + private static boolean sampleObjectAllocations; + private static long objectAllocationsSamplingInterval; static { final long pageSize = Unsafe.getUnsafe().pageSize(); @@ -139,6 +143,31 @@ public final class Options { return sampleThreads; } + public static synchronized void setSampleObjectAllocations(Boolean sample) { + jvm.setSampleObjectAllocations(sample); + sampleObjectAllocations = sample; + } + + public static synchronized boolean getSampleObjectAllocations() { + return sampleObjectAllocations; + } + + /** + * Set interval of sampling object allocation events + * @param interval the number of newly created objects between two sampling + */ + public static synchronized void setObjectAllocationsSamplingInterval(Long interval) { + if (interval <= 0) { + throw new IllegalArgumentException("interval should be greater than 0"); + } + jvm.setObjectAllocationsSamplingInterval(interval); + objectAllocationsSamplingInterval = interval; + } + + public static synchronized long getObjectAllocationsSamplingInterval() { + return objectAllocationsSamplingInterval; + } + private static synchronized void reset() { setMaxChunkSize(DEFAULT_MAX_CHUNK_SIZE); setMemorySize(DEFAULT_MEMORY_SIZE); @@ -148,6 +177,8 @@ public final class Options { setSampleThreads(DEFAULT_SAMPLE_THREADS); setStackDepth(DEFAULT_STACK_DEPTH); setThreadBufferSize(DEFAULT_THREAD_BUFFER_SIZE); + setSampleObjectAllocations(DEFAULT_SAMPLE_OBJECT_ALLOCATIONS); + setObjectAllocationsSamplingInterval(DEFAULT_OBJECT_ALLOCATIONS_SAMPLING_INTERVAL); } static synchronized long getWaitInterval() { diff --git a/src/share/classes/jdk/jfr/internal/PlatformRecorder.java b/src/share/classes/jdk/jfr/internal/PlatformRecorder.java index cbea2e45aac08707726395ac15185b42a459ba39..7011541e6a812c629741d73a2e5913dc6a360a95 100644 --- a/src/share/classes/jdk/jfr/internal/PlatformRecorder.java +++ b/src/share/classes/jdk/jfr/internal/PlatformRecorder.java @@ -47,6 +47,7 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.CopyOnWriteArrayList; +import jdk.jfr.EventNames; import jdk.jfr.EventType; import jdk.jfr.FlightRecorder; import jdk.jfr.FlightRecorderListener; @@ -551,4 +552,8 @@ public final class PlatformRecorder { target.setStopTime(endTime); target.setInternalDuration(Duration.between(startTime, endTime)); } + + public boolean isEnabled(String eventName) { + return MetadataRepository.getInstance().isEnabled(eventName); + } } diff --git a/src/share/classes/jdk/jfr/internal/PlatformRecording.java b/src/share/classes/jdk/jfr/internal/PlatformRecording.java index 74707121d37ca09b691c2c5b742d236686eb4081..0e6e1c20f292d1959b641a5efe2371785ffeb9a4 100644 --- a/src/share/classes/jdk/jfr/internal/PlatformRecording.java +++ b/src/share/classes/jdk/jfr/internal/PlatformRecording.java @@ -775,4 +775,8 @@ public final class PlatformRecording implements AutoCloseable { public SafePath getDumpOnExitDirectory() { return this.dumpOnExitDirectory; } + + public boolean isRecorderEnabled(String eventName) { + return recorder.isEnabled(eventName); + } } diff --git a/src/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java b/src/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java index f15053a7caafd02106fd65f288589c68d068da43..a7bd31df36be4e373426a308d8b0392ecd275425 100644 --- a/src/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java +++ b/src/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java @@ -43,6 +43,7 @@ final class DCmdConfigure extends AbstractDCmd { /** * Execute JFR.configure. * + * @param onVMStart if cmd is invoked at the moment when vm is started * @param repositoryPath the path * @param dumpPath path to dump to on fatal error (oom) * @param stackDepth depth of stack traces @@ -51,6 +52,8 @@ final class DCmdConfigure extends AbstractDCmd { * @param threadBufferSize size of thread buffer for events * @param maxChunkSize threshold at which a new chunk is created in the disk repository * @param sampleThreads if thread sampling should be enabled + * @param sampleObjectAllocations if object allocations sampling should be enbaled + * @param objectAllocationsSamplingInterval interval of object allocations samplings * * @return result @@ -59,6 +62,7 @@ final class DCmdConfigure extends AbstractDCmd { */ public String execute ( + Boolean onVMStart, String repositoryPath, String dumpPath, Integer stackDepth, @@ -67,8 +71,9 @@ final class DCmdConfigure extends AbstractDCmd { Long threadBufferSize, Long memorySize, Long maxChunkSize, - Boolean sampleThreads - + Boolean sampleThreads, + Boolean sampleObjectAllocations, + Long objectAllocationsSamplingInterval ) throws DCmdException { if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) { Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdConfigure: repositorypath=" + repositoryPath + @@ -82,6 +87,13 @@ final class DCmdConfigure extends AbstractDCmd { ", samplethreads" + sampleThreads); } + // Check parameters correctness + if (!onVMStart) { + if (sampleObjectAllocations != null || objectAllocationsSamplingInterval != null) { + Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not change sampleObjectAllocations and objectAllocationsSamplingInterval during application's running"); + return getResult(); + } + } boolean updated = false; if (repositoryPath != null) { @@ -152,6 +164,20 @@ final class DCmdConfigure extends AbstractDCmd { updated = true; } + if (objectAllocationsSamplingInterval != null) { + Options.setObjectAllocationsSamplingInterval(objectAllocationsSamplingInterval); + Logger.log(LogTag.JFR, LogLevel.INFO, "object allocations sampling interval set to " + objectAllocationsSamplingInterval); + printObjectAllocationsSamplingInterval(); + updated = true; + } + + if (sampleObjectAllocations != null) { + Options.setSampleObjectAllocations(sampleObjectAllocations); + Logger.log(LogTag.JFR, LogLevel.INFO, "Sample object allocations set to " + sampleObjectAllocations); + printSampleObjectAllocations(); + updated = true; + } + if (!updated) { println("Current configuration:"); println(); @@ -163,6 +189,8 @@ final class DCmdConfigure extends AbstractDCmd { printMemorySize(); printMaxChunkSize(); printSampleThreads(); + printSampleObjectAllocations(); + printObjectAllocationsSamplingInterval(); } return getResult(); } @@ -214,4 +242,12 @@ final class DCmdConfigure extends AbstractDCmd { printBytes(Options.getMaxChunkSize()); println(); } + + private void printSampleObjectAllocations() { + println("Sample object allocations: " + Options.getSampleObjectAllocations()); + } + + private void printObjectAllocationsSamplingInterval() { + println("objects allocations sampling interval: " + Options.getObjectAllocationsSamplingInterval()); + } } diff --git a/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java b/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java index c198e9b03eac1d9fc2e687be7d180557bc16c29d..09bd717c18710cf8a0d454b2236715c03d575c7a 100644 --- a/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java +++ b/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java @@ -36,12 +36,14 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import jdk.jfr.EventNames; import jdk.jfr.FlightRecorder; import jdk.jfr.Recording; import jdk.jfr.internal.JVM; import jdk.jfr.internal.LogLevel; import jdk.jfr.internal.LogTag; import jdk.jfr.internal.Logger; +import jdk.jfr.internal.Options; import jdk.jfr.internal.OldObjectSample; import jdk.jfr.internal.PrivateAccess; import jdk.jfr.internal.SecuritySupport.SafePath; @@ -116,7 +118,7 @@ final class DCmdStart extends AbstractDCmd { } catch(FileNotFoundException e) { throw new DCmdException("Could not find settings file'" + configName + "'", e); } catch (IOException | ParseException e) { - throw new DCmdException("Could not parse settings file '" + settings[0] + "'", e); + throw new DCmdException("Could not parse settings file '" + configName + "'", e); } } @@ -151,6 +153,14 @@ final class DCmdStart extends AbstractDCmd { recording.setSettings(s); SafePath safePath = null; + if (recording.isRecorderEnabled(EventNames.OptoInstanceObjectAllocation) || + recording.isRecorderEnabled(EventNames.OptoArrayObjectAllocation)) { + if (!Options.getSampleObjectAllocations()) { + println("Please add -XX:FlightRecorderOptions=sampleobjectallocations=true in JVM options."); + return getResult(); + } + } + if (path != null) { try { if (dumpOnExit == null) { diff --git a/test/jdk/jfr/event/objectsprofiling/TestOptoObjectAllocationsSampling.java b/test/jdk/jfr/event/objectsprofiling/TestOptoObjectAllocationsSampling.java new file mode 100644 index 0000000000000000000000000000000000000000..2dbee122d55ce62d018273bcb0c84977b495c3f2 --- /dev/null +++ b/test/jdk/jfr/event/objectsprofiling/TestOptoObjectAllocationsSampling.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. 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. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + * + */ +package jfr.event.objectsprofiling; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.jfr.consumer.RecordedFrame; + +import sun.hotspot.WhiteBox; + +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.Utils; + +/* + * @test TestOptoObjectAllocationsSampling + * @library /lib / + * + * @build sun.hotspot.WhiteBox + * @build ClassFileInstaller + * + * @compile -g TestOptoObjectAllocationsSampling.java + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-TieredCompilation + * -XX:+LogJFR -XX:FlightRecorderOptions=sampleobjectallocations=true,objectallocationssamplinginterval=1 + * jfr.event.objectsprofiling.TestOptoObjectAllocationsSampling + */ +public class TestOptoObjectAllocationsSampling { + private static final WhiteBox WB; + private static final Method OPTO_METHOD; + private static final int OPTO_METHOD_INVOKE_COUNT = 10; + private static final String OPTO_METHOD_NAME = "fireOptoAllocations"; + private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; + private static final int RECORDED_ARRAY_CLASS_OBJECT_SIZE_MAGIC_CODE = 0x1111baba; + + static { + try { + WB = WhiteBox.getWhiteBox(); + Class thisClass = TestOptoObjectAllocationsSampling.class; + OPTO_METHOD = thisClass.getMethod(OPTO_METHOD_NAME, Integer.TYPE, Byte.TYPE); + } catch (Exception e) { + Asserts.fail(e.getMessage()); + throw new ExceptionInInitializerError(e); + } + } + + private static class InstanceObject { + private byte content; + + InstanceObject(byte content) { + this.content = content; + } + + public String toString() { + return "InstanceObject ( " + content + " )"; + } + } + + public static Object array; + public static Object instance; + + public static void fireOptoAllocations(int arrayLength, byte b) { + array = new InstanceObject[arrayLength]; + instance = new InstanceObject(b); + } + + private static void ensureOptoMethod() throws Exception { + int initialCompLevel = WB.getMethodCompilationLevel(OPTO_METHOD); + if (initialCompLevel != COMP_LEVEL_FULL_OPTIMIZATION) { + WB.enqueueMethodForCompilation(OPTO_METHOD, COMP_LEVEL_FULL_OPTIMIZATION); + } + Utils.waitForCondition(() -> COMP_LEVEL_FULL_OPTIMIZATION == WB.getMethodCompilationLevel(OPTO_METHOD), 30_000L); + System.out.format("%s is already compiled at full optimization compile level\n", OPTO_METHOD_NAME); + } + + private static void assertOptoMethod() throws Exception { + int compLevel = WB.getMethodCompilationLevel(OPTO_METHOD); + Asserts.assertTrue(compLevel == COMP_LEVEL_FULL_OPTIMIZATION); + } + + private static void runForLong(int arg1, byte arg2) { + for (int i = 0; i < 1000_000; i++) { + try { + ensureOptoMethod(); + fireOptoAllocations(arg1, arg2); + TimeUnit.SECONDS.sleep(1); + } catch (Exception e) { + } + } + } + + public static void main(String[] args) throws Exception { + int arg1 = 3*1024; + byte arg2 = (byte)0x66; + + // Run warm code to prevent deoptimizaiton. + // It will deoptimize if it runs without this warmup step. + fireOptoAllocations(arg1, arg2); + ensureOptoMethod(); + assertOptoMethod(); + + // runForLong(arg1, arg2); + + try (Recording recording = new Recording()) { + recording.enable(EventNames.OptoArrayObjectAllocation); + recording.enable(EventNames.OptoInstanceObjectAllocation); + + recording.start(); + + // fireOptoAllocationsMethod would be deoptimized after jfr recording is started. + // The root cause is not clear. + // Invoke ensureOptoMethod blindly to enforce it in level 4 again. + ensureOptoMethod(); + + for (int i = 0; i < OPTO_METHOD_INVOKE_COUNT; i++) { + assertOptoMethod(); + fireOptoAllocations(arg1, arg2); + WB.youngGC(); + } + + recording.stop(); + + List events = Events.fromRecording(recording); + int countOfInstanceObject = 0; + int countOfArrayObject = 0; + final String instanceObjectClassName = InstanceObject.class.getName(); + final String arrayObjectClassName = InstanceObject[].class.getName(); + + for (RecordedEvent event : events) { + RecordedStackTrace stackTrace = event.getStackTrace(); + Asserts.assertTrue(stackTrace != null); + List frames = stackTrace.getFrames(); + Asserts.assertTrue(frames != null && frames.size() > 0); + RecordedFrame topFrame = frames.get(0); + Asserts.assertTrue(event.hasField("objectClass")); + RecordedClass clazz = event.getValue("objectClass"); + String className = clazz.getName(); + Asserts.assertTrue(event.getStackTrace().getFrames().size() > 0); + int objectSize = clazz.getObjectSize(); + System.out.format("Allocation Object Class Name: %s, Object Size: %x, topFrame: %s\n", className, objectSize, topFrame.getMethod()); + if (className.equals(instanceObjectClassName)) { + Asserts.assertTrue(!clazz.isArray()); + Asserts.assertTrue(objectSize > 0); + Asserts.assertTrue(topFrame.getLineNumber() > 0); + Asserts.assertTrue(topFrame.getBytecodeIndex() > 0); + countOfInstanceObject++; + } else if (className.equals(arrayObjectClassName)) { + Asserts.assertTrue(clazz.isArray()); + Asserts.assertTrue(objectSize == RECORDED_ARRAY_CLASS_OBJECT_SIZE_MAGIC_CODE); + countOfArrayObject++; + Asserts.assertTrue(topFrame.getLineNumber() > 0); + Asserts.assertTrue(topFrame.getBytecodeIndex() > 0); + } + } + System.out.format("Total Event Count: %d, EventOptoInstanceObjectAllocaiton Count: %d, EventOptoArrayObjectAllocation Count: %d\n", events.size(), countOfInstanceObject, countOfArrayObject); + + Asserts.assertTrue(countOfArrayObject == countOfInstanceObject); + Asserts.assertTrue(countOfArrayObject == OPTO_METHOD_INVOKE_COUNT); + Asserts.assertTrue(events.size() >= (countOfInstanceObject + countOfArrayObject)); + } + } +} diff --git a/test/lib/jdk/test/lib/jfr/EventNames.java b/test/lib/jdk/test/lib/jfr/EventNames.java index 3f37ecb88f2ab312e2086829cf4405ae03971280..ba5b4cc833386dba4388c0323e9ef2bc5ee0ff10 100644 --- a/test/lib/jdk/test/lib/jfr/EventNames.java +++ b/test/lib/jdk/test/lib/jfr/EventNames.java @@ -176,6 +176,9 @@ public class EventNames { public final static String ActiveRecording = PREFIX + "ActiveRecording"; public final static String ActiveSetting = PREFIX + "ActiveSetting"; + public final static String OptoInstanceObjectAllocation = PREFIX + "OptoInstanceObjectAllocation"; + public final static String OptoArrayObjectAllocation = PREFIX + "OptoArrayObjectAllocation"; + public static boolean isGcEvent(EventType et) { return et.getCategoryNames().contains(GC_CATEGORY); }