乐趣区

聊聊openjdk的BufferPoolMXBean


本文主要研究一下 openjdk 的 BufferPoolMXBean
PlatformManagedObject
java.management/java/lang/management/PlatformManagedObject.java
public interface PlatformManagedObject {
/**
* Returns an {@link ObjectName ObjectName} instance representing
* the object name of this platform managed object.
*
* @return an {@link ObjectName ObjectName} instance representing
* the object name of this platform managed object.
*/
public ObjectName getObjectName();
}
PlatformManagedObject 接口定义了 getObjectName 方法用于返回 ObjectName
BufferPoolMXBean
java.management/java/lang/management/BufferPoolMXBean.java
public interface BufferPoolMXBean extends PlatformManagedObject {

/**
* Returns the name representing this buffer pool.
*
* @return The name of this buffer pool.
*/
String getName();

/**
* Returns an estimate of the number of buffers in the pool.
*
* @return An estimate of the number of buffers in this pool
*/
long getCount();

/**
* Returns an estimate of the total capacity of the buffers in this pool.
* A buffer’s capacity is the number of elements it contains and the value
* returned by this method is an estimate of the total capacity of buffers
* in the pool in bytes.
*
* @return An estimate of the total capacity of the buffers in this pool
* in bytes
*/
long getTotalCapacity();

/**
* Returns an estimate of the memory that the Java virtual machine is using
* for this buffer pool. The value returned by this method may differ
* from the estimate of the total {@link #getTotalCapacity capacity} of
* the buffers in this pool. This difference is explained by alignment,
* memory allocator, and other implementation specific reasons.
*
* @return An estimate of the memory that the Java virtual machine is using
* for this buffer pool in bytes, or {@code -1L} if an estimate of
* the memory usage is not available
*/
long getMemoryUsed();
}
BufferPoolMXBean 接口继承了 PlatformManagedObject,它定义了 getName、getCount、getTotalCapacity、getMemoryUsed 方法
ManagementFactoryHelper
java.management/sun/management/ManagementFactoryHelper.java
public class ManagementFactoryHelper {
static {
// make sure that the management lib is loaded within
// java.lang.management.ManagementFactory
jdk.internal.misc.Unsafe.getUnsafe().ensureClassInitialized(ManagementFactory.class);
}

private static final VMManagement jvm = new VMManagementImpl();

private ManagementFactoryHelper() {};

public static VMManagement getVMManagement() {
return jvm;
}

static final String LOGGING_MXBEAN_NAME = “java.util.logging:type=Logging”;
private static ClassLoadingImpl classMBean = null;
private static MemoryImpl memoryMBean = null;
private static ThreadImpl threadMBean = null;
private static RuntimeImpl runtimeMBean = null;
private static CompilationImpl compileMBean = null;
private static BaseOperatingSystemImpl osMBean = null;

//……

private static List<BufferPoolMXBean> bufferPools = null;
public static synchronized List<BufferPoolMXBean> getBufferPoolMXBeans() {
if (bufferPools == null) {
bufferPools = new ArrayList<>(2);
bufferPools.add(createBufferPoolMXBean(SharedSecrets.getJavaNioAccess()
.getDirectBufferPool()));
bufferPools.add(createBufferPoolMXBean(sun.nio.ch.FileChannelImpl
.getMappedBufferPool()));
}
return bufferPools;
}

private static BufferPoolMXBean
createBufferPoolMXBean(final JavaNioAccess.BufferPool pool)
{
return new BufferPoolMXBean() {
private volatile ObjectName objname; // created lazily
@Override
public ObjectName getObjectName() {
ObjectName result = objname;
if (result == null) {
synchronized (this) {
result = objname;
if (result == null) {
result = Util.newObjectName(BUFFER_POOL_MXBEAN_NAME +
“,name=” + pool.getName());
objname = result;
}
}
}
return result;
}
@Override
public String getName() {
return pool.getName();
}
@Override
public long getCount() {
return pool.getCount();
}
@Override
public long getTotalCapacity() {
return pool.getTotalCapacity();
}
@Override
public long getMemoryUsed() {
return pool.getMemoryUsed();
}
};
}

//……
}

ManagementFactoryHelper 的 getBufferPoolMXBeans 方法会通过 createBufferPoolMXBean 方法创建两个 BufferPoolMXBean,然后添加到 bufferPools
其中一个是 DirectBufferPool,一个是 MappedBufferPool;他们分别使用 SharedSecrets.getJavaNioAccess().getDirectBufferPool() 以及 sun.nio.ch.FileChannelImpl.getMappedBufferPool() 来创建
createBufferPoolMXBean 方法使用匿名类创建了 BufferPoolMXBean 的实现;createBufferPoolMXBean 方法接收 JavaNioAccess.BufferPool 参数,其 getCount、getTotalCapacity、getMemoryUsed 等均是直接使用 pool 的相关方法

JavaNioAccess.BufferPool
java.base/jdk/internal/access/JavaNioAccess.java
public interface JavaNioAccess {
/**
* Provides access to information on buffer usage.
*/
interface BufferPool {
String getName();
long getCount();
long getTotalCapacity();
long getMemoryUsed();
}
BufferPool getDirectBufferPool();
}
JavaNioAccess 里头定义了 BufferPool 接口,它定义了 getName、getCount、getTotalCapacity、getMemoryUsed 方法;除此之外 JavaNioAccess 还定义了 getDirectBufferPool 方法用于返回 BufferPool
SharedSecrets
java.base/jdk/internal/access/SharedSecrets.java
public class SharedSecrets {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static JavaUtilJarAccess javaUtilJarAccess;
private static JavaLangAccess javaLangAccess;
private static JavaLangModuleAccess javaLangModuleAccess;
private static JavaLangInvokeAccess javaLangInvokeAccess;
private static JavaLangRefAccess javaLangRefAccess;
private static JavaIOAccess javaIOAccess;
private static JavaNetInetAddressAccess javaNetInetAddressAccess;
private static JavaNetHttpCookieAccess javaNetHttpCookieAccess;
private static JavaNetSocketAccess javaNetSocketAccess;
private static JavaNetUriAccess javaNetUriAccess;
private static JavaNetURLAccess javaNetURLAccess;
private static JavaNetURLClassLoaderAccess javaNetURLClassLoaderAccess;
private static JavaNioAccess javaNioAccess;
private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
private static JavaIOFilePermissionAccess javaIOFilePermissionAccess;
private static JavaSecurityAccess javaSecurityAccess;
private static JavaUtilZipFileAccess javaUtilZipFileAccess;
private static JavaUtilResourceBundleAccess javaUtilResourceBundleAccess;
private static JavaAWTAccess javaAWTAccess;
private static JavaAWTFontAccess javaAWTFontAccess;
private static JavaBeansAccess javaBeansAccess;
private static JavaObjectInputStreamAccess javaObjectInputStreamAccess;
private static JavaObjectInputFilterAccess javaObjectInputFilterAccess;
private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess;
private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess;

//……

public static void setJavaNioAccess(JavaNioAccess jna) {
javaNioAccess = jna;
}

public static JavaNioAccess getJavaNioAccess() {
if (javaNioAccess == null) {
// Ensure java.nio.Buffer is initialized, which provides the
// shared secret.
unsafe.ensureClassInitialized(java.nio.Buffer.class);
}
return javaNioAccess;
}

//……
}
SharedSecrets 提供了 JavaNioAccess 的 getter 及 setter
Buffer
java.base/java/nio/Buffer.java
public abstract class Buffer {
// Cached unsafe-access object
static final Unsafe UNSAFE = Unsafe.getUnsafe();

/**
* The characteristics of Spliterators that traverse and split elements
* maintained in Buffers.
*/
static final int SPLITERATOR_CHARACTERISTICS =
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;

// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;

// Used by heap byte buffers or direct buffers with Unsafe access
// For heap byte buffers this field will be the address relative to the
// array base address and offset into that array. The address might
// not align on a word boundary for slices, nor align at a long word
// (8 byte) boundary for byte[] allocations on 32-bit systems.
// For direct buffers it is the start address of the memory region. The
// address might not align on a word boundary for slices, nor when created
// using JNI, see NewDirectByteBuffer(void*, long).
// Should ideally be declared final
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
long address;

//……

static {
// setup access to this package in SharedSecrets
SharedSecrets.setJavaNioAccess(
new JavaNioAccess() {
@Override
public JavaNioAccess.BufferPool getDirectBufferPool() {
return Bits.BUFFER_POOL;
}
});
}

}
抽象类 Buffer 有个 static 代码块,里头创建了匿名 JavaNioAccess,然后设置到了 SharedSecrets 中;其中匿名 JavaNioAccess 的 getDirectBufferPool 方法返回的是 Bits.BUFFER_POOL
FileChannelImpl
java.base/sun/nio/ch/FileChannelImpl.java
public class FileChannelImpl
extends FileChannel
{
// Memory allocation size for mapping buffers
private static final long allocationGranularity;

// Access to FileDescriptor internals
private static final JavaIOFileDescriptorAccess fdAccess =
SharedSecrets.getJavaIOFileDescriptorAccess();

// Used to make native read and write calls
private final FileDispatcher nd;

// File descriptor
private final FileDescriptor fd;

//……

// — Memory-mapped buffers —

private static class Unmapper
implements Runnable
{
// may be required to close file
private static final NativeDispatcher nd = new FileDispatcherImpl();

// keep track of mapped buffer usage
static volatile int count;
static volatile long totalSize;
static volatile long totalCapacity;

private volatile long address;
private final long size;
private final int cap;
private final FileDescriptor fd;

private Unmapper(long address, long size, int cap,
FileDescriptor fd)
{
assert (address != 0);
this.address = address;
this.size = size;
this.cap = cap;
this.fd = fd;

synchronized (Unmapper.class) {
count++;
totalSize += size;
totalCapacity += cap;
}
}

public void run() {
if (address == 0)
return;
unmap0(address, size);
address = 0;

// if this mapping has a valid file descriptor then we close it
if (fd.valid()) {
try {
nd.close(fd);
} catch (IOException ignore) {
// nothing we can do
}
}

synchronized (Unmapper.class) {
count–;
totalSize -= size;
totalCapacity -= cap;
}
}
}

//……

/**
* Invoked by sun.management.ManagementFactoryHelper to create the management
* interface for mapped buffers.
*/
public static JavaNioAccess.BufferPool getMappedBufferPool() {
return new JavaNioAccess.BufferPool() {
@Override
public String getName() {
return “mapped”;
}
@Override
public long getCount() {
return Unmapper.count;
}
@Override
public long getTotalCapacity() {
return Unmapper.totalCapacity;
}
@Override
public long getMemoryUsed() {
return Unmapper.totalSize;
}
};
}

//……
}
FileChannelImpl 定义了 getMappedBufferPool 方法,返回的是匿名 JavaNioAccess.BufferPool,其相关返回实现直接使用 Unmapper 的对应方法;Unmapper 实现了 Runnable 接口
小结

BufferPoolMXBean 接口继承了 PlatformManagedObject,它定义了 getName、getCount、getTotalCapacity、getMemoryUsed 方法
ManagementFactoryHelper 的 getBufferPoolMXBeans 方法会通过 createBufferPoolMXBean 方法创建两个 BufferPoolMXBean,然后添加到 bufferPools;其中一个是 DirectBufferPool,一个是 MappedBufferPool;他们分别使用 SharedSecrets.getJavaNioAccess().getDirectBufferPool() 以及 sun.nio.ch.FileChannelImpl.getMappedBufferPool() 来创建
createBufferPoolMXBean 方法接收 JavaNioAccess.BufferPool 参数;抽象类 Buffer 有个 static 代码块,里头创建了匿名 JavaNioAccess,然后设置到了 SharedSecrets 中;其中匿名 JavaNioAccess 的 getDirectBufferPool 方法返回的是 Bits.BUFFER_POOL;FileChannelImpl 定义了 getMappedBufferPool 方法,返回的是匿名 JavaNioAccess.BufferPool,其相关返回实现直接使用 Unmapper 的对应方法;Unmapper 实现了 Runnable 接口

doc
Interface BufferPoolMXBean

退出移动版