From 0c812b3150a95155b54b4ca6ec3ee44aab52046b Mon Sep 17 00:00:00 2001 From: alanb Date: Tue, 26 Aug 2008 10:21:22 +0100 Subject: [PATCH] 6682020: (bf) Support monitoring of direct and mapped buffer usage Reviewed-by: mchung, iris --- make/java/java/FILES_java.gmk | 3 +- make/java/nio/FILES_java.gmk | 1 + .../lang/management/PlatformComponent.java | 18 +++ src/share/classes/java/nio/Bits.java | 66 ++++++++++- .../classes/java/nio/BufferPoolMXBean.java | 94 ++++++++++++++++ .../classes/java/nio/Direct-X-Buffer.java | 18 +-- src/share/classes/sun/misc/JavaNioAccess.java | 32 ++++++ src/share/classes/sun/misc/SharedSecrets.java | 15 +++ .../classes/sun/nio/ch/FileChannelImpl.java | 71 +++++++++++- test/java/nio/BufferPoolMXBean/Basic.java | 106 ++++++++++++++++++ 10 files changed, 409 insertions(+), 15 deletions(-) create mode 100644 src/share/classes/java/nio/BufferPoolMXBean.java create mode 100644 src/share/classes/sun/misc/JavaNioAccess.java create mode 100644 test/java/nio/BufferPoolMXBean/Basic.java diff --git a/make/java/java/FILES_java.gmk b/make/java/java/FILES_java.gmk index b4a07fc35..2531ce723 100644 --- a/make/java/java/FILES_java.gmk +++ b/make/java/java/FILES_java.gmk @@ -449,6 +449,7 @@ JAVA_JAVA_java = \ 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) diff --git a/make/java/nio/FILES_java.gmk b/make/java/nio/FILES_java.gmk index 5958812ad..c99f55204 100644 --- a/make/java/nio/FILES_java.gmk +++ b/make/java/nio/FILES_java.gmk @@ -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 \ diff --git a/src/share/classes/java/lang/management/PlatformComponent.java b/src/share/classes/java/lang/management/PlatformComponent.java index 3cd5d369d..a90d0c7c5 100644 --- a/src/share/classes/java/lang/management/PlatformComponent.java +++ b/src/share/classes/java/lang/management/PlatformComponent.java @@ -32,6 +32,7 @@ import java.util.HashSet; 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() { + public List getMXBeans() { + List pools = new ArrayList(2); + pools.add( sun.misc.SharedSecrets.getJavaNioAccess().getDirectBufferPoolMXBean() ); + pools.add( sun.nio.ch.FileChannelImpl.getMappedBufferPoolMXBean() ); + return pools; + } + }), + + // Sun Platform Extension /** diff --git a/src/share/classes/java/nio/Bits.java b/src/share/classes/java/nio/Bits.java index 603ee11bb..ee74686c9 100644 --- a/src/share/classes/java/nio/Bits.java +++ b/src/share/classes/java/nio/Bits.java @@ -29,6 +29,8 @@ import java.security.AccessController; 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 { // package-private // direct buffer memory. This value may be changed during VM // initialization if it is launched with "-XX:MaxDirectMemorySize=". 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 @@ class Bits { // package-private } if (size <= maxMemory - reservedMemory) { reservedMemory += size; + usedMemory += cap; + count++; return; } } @@ -655,17 +661,71 @@ class Bits { // package-private 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 --git a/src/share/classes/java/nio/BufferPoolMXBean.java b/src/share/classes/java/nio/BufferPoolMXBean.java new file mode 100644 index 000000000..0a7fe303f --- /dev/null +++ b/src/share/classes/java/nio/BufferPoolMXBean.java @@ -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. + * + *

A class implementing this interface is an MXBean. 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: + *

+ *     List<BufferPoolMXBean> pools = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
+ * 
+ * + *

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: + *

+ * java.nio:type=BufferPool,name=pool name + *
+ * where pool name 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(); +} diff --git a/src/share/classes/java/nio/Direct-X-Buffer.java b/src/share/classes/java/nio/Direct-X-Buffer.java index 092ac0e83..0ad03feef 100644 --- a/src/share/classes/java/nio/Direct-X-Buffer.java +++ b/src/share/classes/java/nio/Direct-X-Buffer.java @@ -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 --git a/src/share/classes/sun/misc/JavaNioAccess.java b/src/share/classes/sun/misc/JavaNioAccess.java new file mode 100644 index 000000000..4781cb7b4 --- /dev/null +++ b/src/share/classes/sun/misc/JavaNioAccess.java @@ -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 --git a/src/share/classes/sun/misc/SharedSecrets.java b/src/share/classes/sun/misc/SharedSecrets.java index 3a9db2364..b9de220bb 100644 --- a/src/share/classes/sun/misc/SharedSecrets.java +++ b/src/share/classes/sun/misc/SharedSecrets.java @@ -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() { @@ -77,6 +78,20 @@ public class SharedSecrets { 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; } diff --git a/src/share/classes/sun/nio/ch/FileChannelImpl.java b/src/share/classes/sun/nio/ch/FileChannelImpl.java index c4ac22c04..894c489cd 100644 --- a/src/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/share/classes/sun/nio/ch/FileChannelImpl.java @@ -32,6 +32,7 @@ import java.io.RandomAccessFile; 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.ref.ReferenceQueue; 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 @@ 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 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 @@ 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 --git a/test/java/nio/BufferPoolMXBean/Basic.java b/test/java/nio/BufferPoolMXBean/Basic.java new file mode 100644 index 000000000..98e5f0e0b --- /dev/null +++ b/test/java/nio/BufferPoolMXBean/Basic.java @@ -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 buffers; + static MappedByteBuffer mbb; + + // check counters + static void check(List 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(bufferCount); + long totalCapacity = 0L; + for (int i=0; i pools = + ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class); + check(pools, bufferCount, totalCapacity); + + // using MBeanServer + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + Set mbeans = server.queryNames( + new ObjectName("java.nio:type=BufferPool,*"), null); + pools = new ArrayList(); + for (ObjectName name: mbeans) { + BufferPoolMXBean pool = ManagementFactory + .newPlatformMXBeanProxy(server, name.toString(), BufferPoolMXBean.class); + pools.add(pool); + } + check(pools, bufferCount, totalCapacity); + } +} -- GitLab