< prev index next >
src/share/classes/sun/nio/ch/Util.java
Print this page
rev 11524 : 8147468: Allow users to bound the size of buffers cached in the per-thread buffer caches
Summary: Introduces the jdk.nio.maxCachedBufferSize property.
Reviewed-by: alanb, bpb
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -45,10 +45,13 @@
// -- Caches --
// The number of temp buffers in our pool
private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX;
+ // The max size allowed for a cached temp buffer, in bytes
+ private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize();
+
// Per-thread cache of temporary direct buffers
private static ThreadLocal<BufferCache> bufferCache =
new ThreadLocal<BufferCache>()
{
@Override
@@ -56,10 +59,56 @@
return new BufferCache();
}
};
/**
+ * Returns the max size allowed for a cached temp buffers, in
+ * bytes. It defaults to Long.MAX_VALUE. It can be set with the
+ * jdk.nio.maxCachedBufferSize property. Even though
+ * ByteBuffer.capacity() returns an int, we're using a long here
+ * for potential future-proofing.
+ */
+ private static long getMaxCachedBufferSize() {
+ String s = java.security.AccessController.doPrivileged(
+ new PrivilegedAction<String>() {
+ @Override
+ public String run() {
+ return System.getProperty("jdk.nio.maxCachedBufferSize");
+ }
+ });
+ if (s != null) {
+ try {
+ long m = Long.parseLong(s);
+ if (m >= 0) {
+ return m;
+ } else {
+ // if it's negative, ignore the system property
+ }
+ } catch (NumberFormatException e) {
+ // if the string is not well formed, ignore the system property
+ }
+ }
+ return Long.MAX_VALUE;
+ }
+
+ /**
+ * Returns true if a buffer of this size is too large to be
+ * added to the buffer cache, false otherwise.
+ */
+ private static boolean isBufferTooLarge(int size) {
+ return size > MAX_CACHED_BUFFER_SIZE;
+ }
+
+ /**
+ * Returns true if the buffer is too large to be added to the
+ * buffer cache, false otherwise.
+ */
+ private static boolean isBufferTooLarge(ByteBuffer buf) {
+ return isBufferTooLarge(buf.capacity());
+ }
+
+ /**
* A simple cache of direct buffers.
*/
private static class BufferCache {
// the array of buffers
private ByteBuffer[] buffers;
@@ -81,10 +130,13 @@
/**
* Removes and returns a buffer from the cache of at least the given
* size (or null if no suitable buffer is found).
*/
ByteBuffer get(int size) {
+ // Don't call this if the buffer would be too large.
+ assert !isBufferTooLarge(size);
+
if (count == 0)
return null; // cache is empty
ByteBuffer[] buffers = this.buffers;
@@ -118,10 +170,13 @@
buf.limit(size);
return buf;
}
boolean offerFirst(ByteBuffer buf) {
+ // Don't call this if the buffer is too large.
+ assert !isBufferTooLarge(buf);
+
if (count >= TEMP_BUF_POOL_SIZE) {
return false;
} else {
start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;
buffers[start] = buf;
@@ -129,10 +184,13 @@
return true;
}
}
boolean offerLast(ByteBuffer buf) {
+ // Don't call this if the buffer is too large.
+ assert !isBufferTooLarge(buf);
+
if (count >= TEMP_BUF_POOL_SIZE) {
return false;
} else {
int next = (start + count) % TEMP_BUF_POOL_SIZE;
buffers[next] = buf;
@@ -157,10 +215,19 @@
/**
* Returns a temporary buffer of at least the given size
*/
public static ByteBuffer getTemporaryDirectBuffer(int size) {
+ // If a buffer of this size is too large for the cache, there
+ // should not be a buffer in the cache that is at least as
+ // large. So we'll just create a new one. Also, we don't have
+ // to remove the buffer from the cache (as this method does
+ // below) given that we won't put the new buffer in the cache.
+ if (isBufferTooLarge(size)) {
+ return ByteBuffer.allocateDirect(size);
+ }
+
BufferCache cache = bufferCache.get();
ByteBuffer buf = cache.get(size);
if (buf != null) {
return buf;
} else {
@@ -186,10 +253,17 @@
* Releases a temporary buffer by returning to the cache or freeing it. If
* returning to the cache then insert it at the start so that it is
* likely to be returned by a subsequent call to getTemporaryDirectBuffer.
*/
static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {
+ // If the buffer is too large for the cache we don't have to
+ // check the cache. We'll just free it.
+ if (isBufferTooLarge(buf)) {
+ free(buf);
+ return;
+ }
+
assert buf != null;
BufferCache cache = bufferCache.get();
if (!cache.offerFirst(buf)) {
// cache is full
free(buf);
@@ -201,10 +275,17 @@
* returning to the cache then insert it at the end. This makes it
* suitable for scatter/gather operations where the buffers are returned to
* cache in same order that they were obtained.
*/
static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {
+ // If the buffer is too large for the cache we don't have to
+ // check the cache. We'll just free it.
+ if (isBufferTooLarge(buf)) {
+ free(buf);
+ return;
+ }
+
assert buf != null;
BufferCache cache = bufferCache.get();
if (!cache.offerLast(buf)) {
// cache is full
free(buf);
< prev index next >