6682020: (bf) Support monitoring of direct and mapped buffer usage
Reviewed-by: mchung, iris
--- a/jdk/make/java/java/FILES_java.gmk Tue Aug 26 09:23:12 2008 +0100
+++ b/jdk/make/java/java/FILES_java.gmk Tue Aug 26 10:21:22 2008 +0100
@@ -449,6 +449,7 @@
sun/misc/JavaLangAccess.java \
sun/misc/JavaIOAccess.java \
sun/misc/JavaIODeleteOnExitAccess.java \
- sun/misc/JavaIOFileDescriptorAccess.java
+ sun/misc/JavaIOFileDescriptorAccess.java \
+ sun/misc/JavaNioAccess.java
FILES_java = $(JAVA_JAVA_java)
--- a/jdk/make/java/nio/FILES_java.gmk Tue Aug 26 09:23:12 2008 +0100
+++ b/jdk/make/java/nio/FILES_java.gmk Tue Aug 26 10:21:22 2008 +0100
@@ -26,6 +26,7 @@
FILES_src = \
java/nio/Bits.java \
java/nio/Buffer.java \
+ java/nio/BufferPoolMXBean.java \
java/nio/ByteOrder.java \
java/nio/MappedByteBuffer.java \
java/nio/StringCharBuffer.java \
--- a/jdk/src/share/classes/java/lang/management/PlatformComponent.java Tue Aug 26 09:23:12 2008 +0100
+++ b/jdk/src/share/classes/java/lang/management/PlatformComponent.java Tue Aug 26 10:21:22 2008 +0100
@@ -32,6 +32,7 @@
import java.util.Set;
import java.util.logging.LoggingMXBean;
import java.util.logging.LogManager;
+import java.nio.BufferPoolMXBean;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
@@ -188,6 +189,23 @@
}
}),
+
+ /**
+ * Buffer pools.
+ */
+ BUFFER_POOL(
+ "java.nio.BufferPoolMXBean",
+ "java.nio", "BufferPool", keyProperties("name"),
+ new MXBeanFetcher<BufferPoolMXBean>() {
+ public List<BufferPoolMXBean> getMXBeans() {
+ List<BufferPoolMXBean> pools = new ArrayList<BufferPoolMXBean>(2);
+ pools.add( sun.misc.SharedSecrets.getJavaNioAccess().getDirectBufferPoolMXBean() );
+ pools.add( sun.nio.ch.FileChannelImpl.getMappedBufferPoolMXBean() );
+ return pools;
+ }
+ }),
+
+
// Sun Platform Extension
/**
--- a/jdk/src/share/classes/java/nio/Bits.java Tue Aug 26 09:23:12 2008 +0100
+++ b/jdk/src/share/classes/java/nio/Bits.java Tue Aug 26 10:21:22 2008 +0100
@@ -29,6 +29,8 @@
import java.security.PrivilegedAction;
import sun.misc.Unsafe;
import sun.misc.VM;
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
/**
* Access to bits, native and otherwise.
@@ -625,13 +627,15 @@
// direct buffer memory. This value may be changed during VM
// initialization if it is launched with "-XX:MaxDirectMemorySize=<size>".
private static volatile long maxMemory = VM.maxDirectMemory();
- private static volatile long reservedMemory = 0;
+ private static volatile long reservedMemory;
+ private static volatile long usedMemory;
+ private static volatile long count;
private static boolean memoryLimitSet = false;
// These methods should be called whenever direct memory is allocated or
// freed. They allow the user to control the amount of direct memory
// which a process may access. All sizes are specified in bytes.
- static void reserveMemory(long size) {
+ static void reserveMemory(long size, int cap) {
synchronized (Bits.class) {
if (!memoryLimitSet && VM.isBooted()) {
@@ -640,6 +644,8 @@
}
if (size <= maxMemory - reservedMemory) {
reservedMemory += size;
+ usedMemory += cap;
+ count++;
return;
}
}
@@ -655,17 +661,71 @@
if (reservedMemory + size > maxMemory)
throw new OutOfMemoryError("Direct buffer memory");
reservedMemory += size;
+ usedMemory += cap;
+ count++;
}
}
- static synchronized void unreserveMemory(long size) {
+ static synchronized void unreserveMemory(long size, int cap) {
if (reservedMemory > 0) {
reservedMemory -= size;
+ usedMemory -= cap;
+ count--;
assert (reservedMemory > -1);
}
}
+ // -- Management interface for monitoring of direct buffer usage --
+
+ static {
+ // setup access to this package in SharedSecrets
+ sun.misc.SharedSecrets.setJavaNioAccess(
+ new sun.misc.JavaNioAccess() {
+ @Override
+ public BufferPoolMXBean getDirectBufferPoolMXBean() {
+ return LazyInitialization.directBufferPoolMXBean;
+ }
+ }
+ );
+ }
+
+ // Lazy initialization of management interface
+ private static class LazyInitialization {
+ static final BufferPoolMXBean directBufferPoolMXBean = directBufferPoolMXBean();
+
+ private static BufferPoolMXBean directBufferPoolMXBean() {
+ final String pool = "direct";
+ final ObjectName obj;
+ try {
+ obj = new ObjectName("java.nio:type=BufferPool,name=" + pool);
+ } catch (MalformedObjectNameException x) {
+ throw new AssertionError(x);
+ }
+ return new BufferPoolMXBean() {
+ @Override
+ public ObjectName getObjectName() {
+ return obj;
+ }
+ @Override
+ public String getName() {
+ return pool;
+ }
+ @Override
+ public long getCount() {
+ return Bits.count;
+ }
+ @Override
+ public long getTotalCapacity() {
+ return Bits.usedMemory;
+ }
+ @Override
+ public long getMemoryUsed() {
+ return Bits.reservedMemory;
+ }
+ };
+ }
+ }
// -- Bulk get/put acceleration --
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/nio/BufferPoolMXBean.java Tue Aug 26 10:21:22 2008 +0100
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.nio;
+
+import java.lang.management.PlatformManagedObject;
+
+/**
+ * The management interface for a buffer pool.
+ *
+ * <p> A class implementing this interface is an <a href=
+ * "java.lang.management.ManagementFactory.html#MXBean">MXBean</a>. A Java
+ * virtual machine has one or more implementations of this interface. The {@link
+ * java.lang.management.ManagementFactory#getPlatformMXBeans getPlatformMXBeans}
+ * method can be used to obtain the list of {@code BufferPoolMXBean} objects
+ * representing the management interfaces for pools of buffers as follows:
+ * <pre>
+ * List<BufferPoolMXBean> pools = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
+ * </pre>
+ *
+ * <p> The management interfaces are also registered with the platform {@link
+ * javax.management.MBeanServer MBeanServer}. The {@link
+ * javax.management.ObjectName ObjectName} that uniquely identifies the
+ * management interface within the {@code MBeanServer} takes the form:
+ * <blockquote>
+ * <tt>java.nio:type=BufferPool</tt><tt>,name=</tt><i>pool name</i>
+ * </blockquote>
+ * where <em>pool name</em> is the {@link #getName name} of the buffer pool.
+ *
+ * @since 1.7
+ */
+
+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();
+}
--- a/jdk/src/share/classes/java/nio/Direct-X-Buffer.java Tue Aug 26 09:23:12 2008 +0100
+++ b/jdk/src/share/classes/java/nio/Direct-X-Buffer.java Tue Aug 26 10:21:22 2008 +0100
@@ -71,11 +71,13 @@
private static Unsafe unsafe = Unsafe.getUnsafe();
private long address;
+ private long size;
private int capacity;
- private Deallocator(long address, int capacity) {
+ private Deallocator(long address, long size, int capacity) {
assert (address != 0);
this.address = address;
+ this.size = size;
this.capacity = capacity;
}
@@ -86,7 +88,7 @@
}
unsafe.freeMemory(address);
address = 0;
- Bits.unreserveMemory(capacity);
+ Bits.unreserveMemory(size, capacity);
}
}
@@ -110,23 +112,25 @@
Direct$Type$Buffer$RW$(int cap) { // package-private
#if[rw]
super(-1, 0, cap, cap, false);
- Bits.reserveMemory(cap);
int ps = Bits.pageSize();
+ int size = cap + ps;
+ Bits.reserveMemory(size, cap);
+
long base = 0;
try {
- base = unsafe.allocateMemory(cap + ps);
+ base = unsafe.allocateMemory(size);
} catch (OutOfMemoryError x) {
- Bits.unreserveMemory(cap);
+ Bits.unreserveMemory(size, cap);
throw x;
}
- unsafe.setMemory(base, cap + ps, (byte) 0);
+ unsafe.setMemory(base, size, (byte) 0);
if (base % ps != 0) {
// Round up to page boundary
address = base + ps - (base & (ps - 1));
} else {
address = base;
}
- cleaner = Cleaner.create(this, new Deallocator(base, cap));
+ cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
#else[rw]
super(cap);
#end[rw]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/misc/JavaNioAccess.java Tue Aug 26 10:21:22 2008 +0100
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.misc;
+
+import java.nio.BufferPoolMXBean;
+
+public interface JavaNioAccess {
+ BufferPoolMXBean getDirectBufferPoolMXBean();
+}
--- a/jdk/src/share/classes/sun/misc/SharedSecrets.java Tue Aug 26 09:23:12 2008 +0100
+++ b/jdk/src/share/classes/sun/misc/SharedSecrets.java Tue Aug 26 10:21:22 2008 +0100
@@ -46,6 +46,7 @@
private static JavaIOAccess javaIOAccess;
private static JavaIODeleteOnExitAccess javaIODeleteOnExitAccess;
private static JavaNetAccess javaNetAccess;
+ private static JavaNioAccess javaNioAccess;
private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
public static JavaUtilJarAccess javaUtilJarAccess() {
@@ -77,6 +78,20 @@
return javaNetAccess;
}
+ public static void setJavaNioAccess(JavaNioAccess jna) {
+ javaNioAccess = jna;
+ }
+
+ public static JavaNioAccess getJavaNioAccess() {
+ if (javaNioAccess == null) {
+ // Ensure java.nio.ByteOrder is initialized; we know that
+ // this class initializes java.nio.Bits that provides the
+ // shared secret.
+ unsafe.ensureClassInitialized(java.nio.ByteOrder.class);
+ }
+ return javaNioAccess;
+ }
+
public static void setJavaIOAccess(JavaIOAccess jia) {
javaIOAccess = jia;
}
--- a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java Tue Aug 26 09:23:12 2008 +0100
+++ b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java Tue Aug 26 10:21:22 2008 +0100
@@ -32,6 +32,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
+import java.nio.BufferPoolMXBean;
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.util.ArrayList;
@@ -43,10 +44,12 @@
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+
import sun.misc.Cleaner;
import sun.security.action.GetPropertyAction;
-
public class FileChannelImpl
extends FileChannel
{
@@ -681,14 +684,26 @@
private static class Unmapper
implements Runnable
{
+ // keep track of mapped buffer usage
+ static volatile int count;
+ static volatile long totalSize;
+ static volatile long totalCapacity;
private long address;
private long size;
+ private int cap;
- private Unmapper(long address, long size) {
+ private Unmapper(long address, long size, int cap) {
assert (address != 0);
this.address = address;
this.size = size;
+ this.cap = cap;
+
+ synchronized (Unmapper.class) {
+ count++;
+ totalSize += size;
+ totalCapacity += cap;
+ }
}
public void run() {
@@ -696,8 +711,13 @@
return;
unmap0(address, size);
address = 0;
+
+ synchronized (Unmapper.class) {
+ count--;
+ totalSize -= size;
+ totalCapacity -= cap;
+ }
}
-
}
private static void unmap(MappedByteBuffer bb) {
@@ -786,7 +806,7 @@
assert (IOStatus.checkAll(addr));
assert (addr % allocationGranularity == 0);
int isize = (int)size;
- Unmapper um = new Unmapper(addr, size + pagePosition);
+ Unmapper um = new Unmapper(addr, size + pagePosition, isize);
if ((!writable) || (imode == MAP_RO))
return Util.newMappedByteBufferR(isize, addr + pagePosition, um);
else
@@ -797,6 +817,49 @@
}
}
+ /**
+ * Returns the management interface for mapped buffers
+ */
+ public static BufferPoolMXBean getMappedBufferPoolMXBean() {
+ return LazyInitialization.mappedBufferPoolMXBean;
+ }
+
+ // Lazy initialization of management interface
+ private static class LazyInitialization {
+ static final BufferPoolMXBean mappedBufferPoolMXBean = mappedBufferPoolMXBean();
+
+ private static BufferPoolMXBean mappedBufferPoolMXBean() {
+ final String pool = "mapped";
+ final ObjectName obj;
+ try {
+ obj = new ObjectName("java.nio:type=BufferPool,name=" + pool);
+ } catch (MalformedObjectNameException x) {
+ throw new AssertionError(x);
+ }
+ return new BufferPoolMXBean() {
+ @Override
+ public ObjectName getObjectName() {
+ return obj;
+ }
+ @Override
+ public String getName() {
+ return pool;
+ }
+ @Override
+ public long getCount() {
+ return Unmapper.count;
+ }
+ @Override
+ public long getTotalCapacity() {
+ return Unmapper.totalCapacity;
+ }
+ @Override
+ public long getMemoryUsed() {
+ return Unmapper.totalSize;
+ }
+ };
+ }
+ }
// -- Locks --
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/BufferPoolMXBean/Basic.java Tue Aug 26 10:21:22 2008 +0100
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc. 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.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 6606598
+ * @summary Unit test for java.nio.BufferPoolMXBean
+ */
+
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.BufferPoolMXBean;
+import java.nio.channels.FileChannel;
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.lang.management.ManagementFactory;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import java.util.*;
+
+public class Basic {
+
+ // static fields to ensure buffers aren't GC'ed
+ static List<ByteBuffer> buffers;
+ static MappedByteBuffer mbb;
+
+ // check counters
+ static void check(List<BufferPoolMXBean> pools,
+ int minBufferCount,
+ long minTotalCapacity)
+ {
+ int bufferCount = 0;
+ long totalCap = 0;
+ long totalMem = 0;
+ for (BufferPoolMXBean pool: pools) {
+ bufferCount += pool.getCount();
+ totalCap += pool.getTotalCapacity();
+ totalMem += pool.getMemoryUsed();
+ }
+ if (bufferCount < minBufferCount)
+ throw new RuntimeException("Count less than expected");
+ if (totalMem < minTotalCapacity)
+ throw new RuntimeException("Memory usage less than expected");
+ if (totalCap < minTotalCapacity)
+ throw new RuntimeException("Total capacity less than expected");
+ }
+
+ public static void main(String[] args) throws Exception {
+ Random rand = new Random();
+
+ // allocate a few direct buffers
+ int bufferCount = 5 + rand.nextInt(20);
+ buffers = new ArrayList<ByteBuffer>(bufferCount);
+ long totalCapacity = 0L;
+ for (int i=0; i<bufferCount; i++) {
+ int cap = 1024 + rand.nextInt(4096);
+ buffers.add( ByteBuffer.allocateDirect(cap) );
+ totalCapacity += cap;
+ }
+
+ // map a file
+ File f = File.createTempFile("blah", null);
+ f.deleteOnExit();
+ RandomAccessFile raf = new RandomAccessFile(f, "rw");
+ FileChannel fc = raf.getChannel();
+ mbb = fc.map(FileChannel.MapMode.READ_WRITE, 10, 100);
+ bufferCount++;
+ totalCapacity += mbb.capacity();
+
+ // direct
+ List<BufferPoolMXBean> pools =
+ ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
+ check(pools, bufferCount, totalCapacity);
+
+ // using MBeanServer
+ MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+ Set<ObjectName> mbeans = server.queryNames(
+ new ObjectName("java.nio:type=BufferPool,*"), null);
+ pools = new ArrayList<BufferPoolMXBean>();
+ for (ObjectName name: mbeans) {
+ BufferPoolMXBean pool = ManagementFactory
+ .newPlatformMXBeanProxy(server, name.toString(), BufferPoolMXBean.class);
+ pools.add(pool);
+ }
+ check(pools, bufferCount, totalCapacity);
+ }
+}