Iris - can you review 6682020? This adds instrumentation so that the
resources for direct and mapped buffers can be monitored by jconsole and
other JMX based tools. It builds on the generic support that Mandy added
last week [1].
Thanks,
-Alan
[1] http://hg.openjdk.java.net/jdk7/tl/jdk/rev/fd563c5dd750
diff -r bcf689d26c1c make/java/nio/FILES_java.gmk
--- a/make/java/nio/FILES_java.gmk Thu Apr 10 16:11:27 2008 -0700
+++ b/make/java/nio/FILES_java.gmk Wed Apr 16 15:02:42 2008 +0100
@@ -26,6 +26,7 @@ FILES_src = \
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 \
diff -r bcf689d26c1c
src/share/classes/java/lang/management/PlatformComponent.java
--- a/src/share/classes/java/lang/management/PlatformComponent.java Thu Apr
10 16:11:27 2008 -0700
+++ b/src/share/classes/java/lang/management/PlatformComponent.java Wed Apr
16 15:02:42 2008 +0100
@@ -32,6 +32,7 @@ import java.util.Set;
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 @@ enum PlatformComponent {
}
}),
+
+ /**
+ * 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
/**
diff -r bcf689d26c1c src/share/classes/java/nio/Bits.java
--- a/src/share/classes/java/nio/Bits.java Thu Apr 10 16:11:27 2008 -0700
+++ b/src/share/classes/java/nio/Bits.java Wed Apr 16 15:02:42 2008 +0100
@@ -29,6 +29,8 @@ import java.security.PrivilegedAction;
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 @@ class Bits {
// 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 = 0L;
+ private static volatile long usedMemory = 0L;
+ private static volatile long count = 0L;
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 @@ class Bits {
}
if (size <= maxMemory - reservedMemory) {
reservedMemory += size;
+ usedMemory += cap;
+ count++;
return;
}
}
@@ -655,17 +661,71 @@ class Bits {
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 --
diff -r bcf689d26c1c src/share/classes/java/nio/BufferPoolMXBean.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/nio/BufferPoolMXBean.java Wed Apr 16 15:02:42
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
[EMAIL PROTECTED]
+ * java.lang.management.ManagementFactory#getPlatformMXBeans
getPlatformMXBeans}
+ * method can be used to obtain the list of [EMAIL PROTECTED]
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 [EMAIL
PROTECTED]
+ * javax.management.MBeanServer MBeanServer}. The [EMAIL PROTECTED]
+ * javax.management.ObjectName ObjectName} that uniquely identifies the
+ * management interface within the [EMAIL PROTECTED] 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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] -1L} if an
estimate of
+ * the memory usage is not available
+ */
+ long getMemoryUsed();
+}
diff -r bcf689d26c1c src/share/classes/java/nio/Direct-X-Buffer.java
--- a/src/share/classes/java/nio/Direct-X-Buffer.java Thu Apr 10 16:11:27
2008 -0700
+++ b/src/share/classes/java/nio/Direct-X-Buffer.java Wed Apr 16 15:02:42
2008 +0100
@@ -71,11 +71,13 @@ class Direct$Type$Buffer$RW$$BO$
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 @@ class Direct$Type$Buffer$RW$$BO$
}
unsafe.freeMemory(address);
address = 0;
- Bits.unreserveMemory(capacity);
+ Bits.unreserveMemory(size, capacity);
}
}
@@ -110,23 +112,25 @@ class Direct$Type$Buffer$RW$$BO$
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]
diff -r bcf689d26c1c src/share/classes/sun/misc/JavaNioAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/misc/JavaNioAccess.java Wed Apr 16 15:02:42
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();
+}
diff -r bcf689d26c1c src/share/classes/sun/misc/SharedSecrets.java
--- a/src/share/classes/sun/misc/SharedSecrets.java Thu Apr 10 16:11:27
2008 -0700
+++ b/src/share/classes/sun/misc/SharedSecrets.java Wed Apr 16 15:02:42
2008 +0100
@@ -46,6 +46,7 @@ public class SharedSecrets {
private static JavaIOAccess javaIOAccess;
private static JavaIODeleteOnExitAccess javaIODeleteOnExitAccess;
private static JavaNetAccess javaNetAccess;
+ private static JavaNioAccess javaNioAccess;
private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
public static JavaUtilJarAccess javaUtilJarAccess() {
@@ -75,6 +76,20 @@ public class SharedSecrets {
public static JavaNetAccess getJavaNetAccess() {
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) {
diff -r bcf689d26c1c src/share/classes/sun/nio/ch/FileChannelImpl.java
--- a/src/share/classes/sun/nio/ch/FileChannelImpl.java Thu Apr 10 16:11:27
2008 -0700
+++ b/src/share/classes/sun/nio/ch/FileChannelImpl.java Wed Apr 16 15:02:42
2008 +0100
@@ -32,6 +32,7 @@ import java.io.IOException;
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,6 +44,8 @@ import java.lang.reflect.Field;
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;
@@ -681,14 +684,26 @@ public class FileChannelImpl
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 Unmapper(long address, long size) {
+ private int cap;
+
+ 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 @@ public class FileChannelImpl
return;
unmap0(address, size);
address = 0;
- }
-
+
+ synchronized (Unmapper.class) {
+ count--;
+ totalSize -= size;
+ totalCapacity -= cap;
+ }
+ }
}
private static void unmap(MappedByteBuffer bb) {
@@ -786,7 +806,7 @@ public class FileChannelImpl
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 @@ public class FileChannelImpl
}
}
+ /**
+ * 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 --
diff -r bcf689d26c1c test/java/nio/BufferPoolMXBean/Basic.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/BufferPoolMXBean/Basic.java Wed Apr 16 15:02:42 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);
+ }
+}