8238665: Add JFR event for direct memory statistics
Reviewed-by: egahlin, alanb
This commit is contained in:
parent
1cc71b41de
commit
a62a0e5282
@ -26,10 +26,10 @@
|
||||
package java.nio;
|
||||
|
||||
import jdk.internal.access.JavaLangRefAccess;
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.misc.VM.BufferPool;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@ -210,7 +210,7 @@ class Bits { // package-private
|
||||
assert cnt >= 0 && reservedMem >= 0 && totalCap >= 0;
|
||||
}
|
||||
|
||||
static final JavaNioAccess.BufferPool BUFFER_POOL = new JavaNioAccess.BufferPool() {
|
||||
static final BufferPool BUFFER_POOL = new BufferPool() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "direct";
|
||||
|
@ -30,6 +30,7 @@ import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.misc.VM.BufferPool;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.util.Spliterator;
|
||||
@ -758,7 +759,7 @@ public abstract class Buffer {
|
||||
SharedSecrets.setJavaNioAccess(
|
||||
new JavaNioAccess() {
|
||||
@Override
|
||||
public JavaNioAccess.BufferPool getDirectBufferPool() {
|
||||
public BufferPool getDirectBufferPool() {
|
||||
return Bits.BUFFER_POOL;
|
||||
}
|
||||
|
||||
|
@ -26,20 +26,16 @@
|
||||
package jdk.internal.access;
|
||||
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.misc.VM.BufferPool;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface JavaNioAccess {
|
||||
|
||||
/**
|
||||
* Provides access to information on buffer usage.
|
||||
* Used by {@code jdk.internal.misc.VM}.
|
||||
*/
|
||||
interface BufferPool {
|
||||
String getName();
|
||||
long getCount();
|
||||
long getTotalCapacity();
|
||||
long getMemoryUsed();
|
||||
}
|
||||
BufferPool getDirectBufferPool();
|
||||
|
||||
/**
|
||||
|
@ -27,10 +27,16 @@ package jdk.internal.misc;
|
||||
|
||||
import static java.lang.Thread.State.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
import sun.nio.ch.FileChannelImpl;
|
||||
|
||||
public class VM {
|
||||
|
||||
// the init level when the VM is fully initialized
|
||||
@ -414,4 +420,34 @@ public class VM {
|
||||
* object class in the archived graph.
|
||||
*/
|
||||
public static native void initializeFromArchive(Class<?> c);
|
||||
|
||||
/**
|
||||
* Provides access to information on buffer usage.
|
||||
*/
|
||||
public interface BufferPool {
|
||||
String getName();
|
||||
long getCount();
|
||||
long getTotalCapacity();
|
||||
long getMemoryUsed();
|
||||
}
|
||||
|
||||
private static class BufferPoolsHolder {
|
||||
static final List<BufferPool> BUFFER_POOLS;
|
||||
|
||||
static {
|
||||
ArrayList<BufferPool> bufferPools = new ArrayList<>(3);
|
||||
bufferPools.add(SharedSecrets.getJavaNioAccess().getDirectBufferPool());
|
||||
bufferPools.add(FileChannelImpl.getMappedBufferPool());
|
||||
bufferPools.add(FileChannelImpl.getSyncMappedBufferPool());
|
||||
|
||||
BUFFER_POOLS = Collections.unmodifiableList(bufferPools);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of buffer pools.
|
||||
*/
|
||||
public static List<BufferPool> getBufferPools() {
|
||||
return BufferPoolsHolder.BUFFER_POOLS;
|
||||
}
|
||||
}
|
||||
|
@ -45,11 +45,11 @@ import java.nio.channels.WritableByteChannel;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.access.JavaIOFileDescriptorAccess;
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.ExtendedMapMode;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.misc.VM.BufferPool;
|
||||
import jdk.internal.ref.Cleaner;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
|
||||
@ -1158,8 +1158,8 @@ public class FileChannelImpl
|
||||
* Invoked by sun.management.ManagementFactoryHelper to create the management
|
||||
* interface for mapped buffers.
|
||||
*/
|
||||
public static JavaNioAccess.BufferPool getMappedBufferPool() {
|
||||
return new JavaNioAccess.BufferPool() {
|
||||
public static BufferPool getMappedBufferPool() {
|
||||
return new BufferPool() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "mapped";
|
||||
@ -1183,8 +1183,8 @@ public class FileChannelImpl
|
||||
* Invoked by sun.management.ManagementFactoryHelper to create the management
|
||||
* interface for sync mapped buffers.
|
||||
*/
|
||||
public static JavaNioAccess.BufferPool getSyncMappedBufferPool() {
|
||||
return new JavaNioAccess.BufferPool() {
|
||||
public static BufferPool getSyncMappedBufferPool() {
|
||||
return new BufferPool() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "mapped - 'non-volatile memory'";
|
||||
|
@ -39,8 +39,9 @@ import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.misc.VM.BufferPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -52,6 +53,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* ManagementFactoryHelper provides static factory methods to create
|
||||
@ -337,16 +339,16 @@ public class ManagementFactoryHelper {
|
||||
static final PlatformLoggingMXBean MBEAN = getInstance();
|
||||
}
|
||||
|
||||
private static List<BufferPoolMXBean> bufferPools = null;
|
||||
public static synchronized List<BufferPoolMXBean> getBufferPoolMXBeans() {
|
||||
private static volatile List<BufferPoolMXBean> bufferPools;
|
||||
public static List<BufferPoolMXBean> getBufferPoolMXBeans() {
|
||||
if (bufferPools == null) {
|
||||
bufferPools = new ArrayList<>(2);
|
||||
bufferPools.add(createBufferPoolMXBean(SharedSecrets.getJavaNioAccess()
|
||||
.getDirectBufferPool()));
|
||||
bufferPools.add(createBufferPoolMXBean(sun.nio.ch.FileChannelImpl
|
||||
.getMappedBufferPool()));
|
||||
bufferPools.add(createBufferPoolMXBean(sun.nio.ch.FileChannelImpl
|
||||
.getSyncMappedBufferPool()));
|
||||
synchronized (ManagementFactoryHelper.class) {
|
||||
if (bufferPools == null) {
|
||||
bufferPools = VM.getBufferPools().stream()
|
||||
.map(ManagementFactoryHelper::createBufferPoolMXBean)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
return bufferPools;
|
||||
}
|
||||
@ -357,7 +359,7 @@ public class ManagementFactoryHelper {
|
||||
* Creates management interface for the given buffer pool.
|
||||
*/
|
||||
private static BufferPoolMXBean
|
||||
createBufferPoolMXBean(final JavaNioAccess.BufferPool pool)
|
||||
createBufferPoolMXBean(final BufferPool pool)
|
||||
{
|
||||
return new BufferPoolMXBean() {
|
||||
private volatile ObjectName objname; // created lazily
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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. Oracle 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.
|
||||
*
|
||||
* 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 jdk.jfr.events;
|
||||
|
||||
import jdk.internal.misc.VM.BufferPool;
|
||||
|
||||
import jdk.jfr.*;
|
||||
import jdk.jfr.internal.Type;
|
||||
|
||||
@Category({ "Java Application", "Statistics" })
|
||||
public abstract class AbstractBufferStatisticsEvent extends AbstractJDKEvent {
|
||||
|
||||
AbstractBufferStatisticsEvent() {
|
||||
BufferPool bufferPool = getBufferPool();
|
||||
|
||||
count = bufferPool.getCount();
|
||||
totalCapacity = bufferPool.getTotalCapacity();
|
||||
memoryUsed = bufferPool.getMemoryUsed();
|
||||
}
|
||||
|
||||
@Label("Count")
|
||||
public long count;
|
||||
|
||||
@Label("Total Capacity")
|
||||
@DataAmount
|
||||
public long totalCapacity;
|
||||
|
||||
@Label("Memory Used")
|
||||
@DataAmount
|
||||
public long memoryUsed;
|
||||
|
||||
abstract BufferPool getBufferPool();
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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. Oracle 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.
|
||||
*
|
||||
* 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 jdk.jfr.events;
|
||||
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.misc.VM.BufferPool;
|
||||
|
||||
import jdk.jfr.*;
|
||||
import jdk.jfr.internal.Type;
|
||||
|
||||
@Name(Type.EVENT_NAME_PREFIX + "DirectBufferStatistics")
|
||||
@Label("Direct Buffer Statistics")
|
||||
@Description("Statistics of direct buffer")
|
||||
public final class DirectBufferStatisticsEvent extends AbstractBufferStatisticsEvent {
|
||||
|
||||
private static final BufferPool DIRECT_BUFFER_POOL = VM.getBufferPools().stream()
|
||||
.filter(p -> "direct".equals(p.getName()))
|
||||
.findFirst().get();
|
||||
|
||||
public DirectBufferStatisticsEvent() {
|
||||
this.maxCapacity = VM.maxDirectMemory();
|
||||
}
|
||||
|
||||
@Label("Maximum Capacity")
|
||||
@Description("Maximum direct buffer capacity the process can use")
|
||||
@DataAmount
|
||||
public long maxCapacity;
|
||||
|
||||
BufferPool getBufferPool() {
|
||||
return DIRECT_BUFFER_POOL;
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import java.util.List;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.events.ActiveRecordingEvent;
|
||||
import jdk.jfr.events.ActiveSettingEvent;
|
||||
import jdk.jfr.events.DirectBufferStatisticsEvent;
|
||||
import jdk.jfr.events.ErrorThrownEvent;
|
||||
import jdk.jfr.events.ExceptionStatisticsEvent;
|
||||
import jdk.jfr.events.ExceptionThrownEvent;
|
||||
@ -76,7 +77,8 @@ public final class JDKEvents {
|
||||
jdk.internal.event.TLSHandshakeEvent.class,
|
||||
jdk.internal.event.X509CertificateEvent.class,
|
||||
jdk.internal.event.X509ValidationEvent.class,
|
||||
jdk.internal.event.ProcessStartEvent.class
|
||||
jdk.internal.event.ProcessStartEvent.class,
|
||||
DirectBufferStatisticsEvent.class
|
||||
};
|
||||
|
||||
// This is a list of the classes with instrumentation code that should be applied.
|
||||
@ -93,6 +95,7 @@ public final class JDKEvents {
|
||||
private static final Class<?>[] targetClasses = new Class<?>[instrumentationClasses.length];
|
||||
private static final JVM jvm = JVM.getJVM();
|
||||
private static final Runnable emitExceptionStatistics = JDKEvents::emitExceptionStatistics;
|
||||
private static final Runnable emitDirectBufferStatistics = JDKEvents::emitDirectBufferStatistics;
|
||||
private static boolean initializationTriggered;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -107,6 +110,7 @@ public final class JDKEvents {
|
||||
}
|
||||
initializationTriggered = true;
|
||||
RequestEngine.addTrustedJDKHook(ExceptionStatisticsEvent.class, emitExceptionStatistics);
|
||||
RequestEngine.addTrustedJDKHook(DirectBufferStatisticsEvent.class, emitDirectBufferStatistics);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Could not initialize JDK events. " + e.getMessage());
|
||||
@ -163,5 +167,11 @@ public final class JDKEvents {
|
||||
|
||||
public static void remove() {
|
||||
RequestEngine.removeHook(JDKEvents::emitExceptionStatistics);
|
||||
RequestEngine.removeHook(emitDirectBufferStatistics);
|
||||
}
|
||||
|
||||
private static void emitDirectBufferStatistics() {
|
||||
DirectBufferStatisticsEvent e = new DirectBufferStatisticsEvent();
|
||||
e.commit();
|
||||
}
|
||||
}
|
||||
|
@ -768,6 +768,10 @@
|
||||
<setting name="stackTrace">true</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.DirectBufferStatistics">
|
||||
<setting name="enabled">true</setting>
|
||||
<setting name="period">5 s</setting>
|
||||
</event>
|
||||
|
||||
|
||||
|
||||
|
@ -768,6 +768,10 @@
|
||||
<setting name="stackTrace">true</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.DirectBufferStatistics">
|
||||
<setting name="enabled">true</setting>
|
||||
<setting name="period">5 s</setting>
|
||||
</event>
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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. Oracle 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.
|
||||
*
|
||||
* 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 jdk.jfr.event.runtime;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.jfr.EventNames;
|
||||
import jdk.test.lib.jfr.Events;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @key jfr
|
||||
* @requires vm.hasJFR
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @run main/othervm -XX:MaxDirectMemorySize=128m jdk.jfr.event.runtime.TestDirectBufferStatisticsEvent
|
||||
* @run main/othervm jdk.jfr.event.runtime.TestDirectBufferStatisticsEvent
|
||||
*/
|
||||
public class TestDirectBufferStatisticsEvent {
|
||||
|
||||
private static final String EVENT_PATH = EventNames.DirectBufferStatistics;
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try (Recording recording = new Recording()) {
|
||||
recording.enable(EVENT_PATH);
|
||||
recording.start();
|
||||
int rounds = 16;
|
||||
int size = 1 * 1024 * 1024; // 1M
|
||||
for (int i = 0; i < rounds; i++) {
|
||||
ByteBuffer.allocateDirect(size);
|
||||
}
|
||||
recording.stop();
|
||||
|
||||
List<RecordedEvent> events = Events.fromRecording(recording);
|
||||
Events.hasEvents(events);
|
||||
|
||||
long count = 0;
|
||||
long totalCapacity = 0;
|
||||
long memoryUsed = 0;
|
||||
|
||||
for (RecordedEvent event : events) {
|
||||
System.out.println(event);
|
||||
Asserts.assertTrue(Events.isEventType(event, EVENT_PATH), "Wrong event type");
|
||||
count = Math.max(count, Events.assertField(event, "count").getValue());
|
||||
totalCapacity = Math.max(totalCapacity, Events.assertField(event, "totalCapacity").getValue());
|
||||
memoryUsed = Math.max(memoryUsed, Events.assertField(event, "memoryUsed").getValue());
|
||||
Asserts.assertEquals(VM.maxDirectMemory(), Events.assertField(event, "maxCapacity").getValue());
|
||||
}
|
||||
|
||||
Asserts.assertGreaterThanOrEqual(count, (long)rounds, "Too few count in statistics event");
|
||||
Asserts.assertGreaterThanOrEqual(totalCapacity, (long)(rounds * size), "Too few totalCapacity in statistics event");
|
||||
Asserts.assertGreaterThanOrEqual(memoryUsed, totalCapacity, "Too few memoryUsed in statistics event");
|
||||
}
|
||||
}
|
||||
}
|
@ -195,6 +195,7 @@ public class EventNames {
|
||||
public final static String X509Certificate = PREFIX + "X509Certificate";
|
||||
public final static String X509Validation = PREFIX + "X509Validation";
|
||||
public final static String SecurityProperty = PREFIX + "SecurityPropertyModification";
|
||||
public final static String DirectBufferStatistics = PREFIX + "DirectBufferStatistics";
|
||||
|
||||
// Flight Recorder
|
||||
public final static String DumpReason = PREFIX + "DumpReason";
|
||||
|
Loading…
x
Reference in New Issue
Block a user