8238665: Add JFR event for direct memory statistics

Reviewed-by: egahlin, alanb
This commit is contained in:
Denghui Dong 2020-04-14 20:36:33 +02:00
parent 1cc71b41de
commit a62a0e5282
13 changed files with 276 additions and 27 deletions

View File

@ -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";

View File

@ -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;
}

View File

@ -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();
/**

View File

@ -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;
}
}

View File

@ -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'";

View File

@ -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

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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");
}
}
}

View File

@ -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";