--- a/jdk/src/share/classes/sun/nio/ch/Util.java Tue Aug 10 13:15:40 2010 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/Util.java Thu Aug 12 19:53:25 2010 +0100
@@ -41,67 +41,180 @@
class Util {
-
// -- Caches --
// The number of temp buffers in our pool
- private static final int TEMP_BUF_POOL_SIZE = 3;
+ private static final int TEMP_BUF_POOL_SIZE = 8;
- // Per-thread soft cache of the last temporary direct buffer
- private static ThreadLocal<SoftReference<ByteBuffer>>[] bufferPool;
+ // Per-thread cache of temporary direct buffers
+ private static ThreadLocal<BufferCache> bufferCache =
+ new ThreadLocal<BufferCache>()
+ {
+ @Override
+ protected BufferCache initialValue() {
+ return new BufferCache();
+ }
+ };
+
+ /**
+ * A simple cache of direct buffers.
+ */
+ private static class BufferCache {
+ // the array of buffers
+ private ByteBuffer[] buffers;
- @SuppressWarnings("unchecked")
- static ThreadLocal<SoftReference<ByteBuffer>>[] createThreadLocalBufferPool() {
- return new ThreadLocal[TEMP_BUF_POOL_SIZE];
- }
+ // the number of buffers in the cache
+ private int count;
+
+ // the index of the first valid buffer (undefined if count == 0)
+ private int start;
+
+ private int next(int i) {
+ return (i + 1) % TEMP_BUF_POOL_SIZE;
+ }
+
+ BufferCache() {
+ buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];
+ }
+
+ /**
+ * 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) {
+ if (count == 0)
+ return null; // cache is empty
- static {
- bufferPool = createThreadLocalBufferPool();
- for (int i=0; i<TEMP_BUF_POOL_SIZE; i++)
- bufferPool[i] = new ThreadLocal<SoftReference<ByteBuffer>>();
- }
+ ByteBuffer[] buffers = this.buffers;
- static ByteBuffer getTemporaryDirectBuffer(int size) {
- ByteBuffer buf = null;
- // Grab a buffer if available
- for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
- SoftReference<ByteBuffer> ref = bufferPool[i].get();
- if ((ref != null) && ((buf = ref.get()) != null) &&
- (buf.capacity() >= size)) {
- buf.rewind();
- buf.limit(size);
- bufferPool[i].set(null);
- return buf;
+ // search for suitable buffer (often the first buffer will do)
+ ByteBuffer buf = buffers[start];
+ if (buf.capacity() < size) {
+ buf = null;
+ int i = start;
+ while ((i = next(i)) != start) {
+ ByteBuffer bb = buffers[i];
+ if (bb == null)
+ break;
+ if (bb.capacity() >= size) {
+ buf = bb;
+ break;
+ }
+ }
+ if (buf == null)
+ return null;
+ // move first element to here to avoid re-packing
+ buffers[i] = buffers[start];
+ }
+
+ // remove first element
+ buffers[start] = null;
+ start = next(start);
+ count--;
+
+ // prepare the buffer and return it
+ buf.rewind();
+ buf.limit(size);
+ return buf;
+ }
+
+ boolean offerFirst(ByteBuffer buf) {
+ if (count >= TEMP_BUF_POOL_SIZE) {
+ return false;
+ } else {
+ start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;
+ buffers[start] = buf;
+ count++;
+ return true;
}
}
- // Make a new one
- return ByteBuffer.allocateDirect(size);
- }
-
- static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
- if (buf == null)
- return;
- // Put it in an empty slot if such exists
- for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
- SoftReference<ByteBuffer> ref = bufferPool[i].get();
- if ((ref == null) || (ref.get() == null)) {
- bufferPool[i].set(new SoftReference<ByteBuffer>(buf));
- return;
- }
- }
- // Otherwise replace a smaller one in the cache if such exists
- for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
- SoftReference<ByteBuffer> ref = bufferPool[i].get();
- ByteBuffer inCacheBuf = ref.get();
- if ((inCacheBuf == null) || (buf.capacity() > inCacheBuf.capacity())) {
- bufferPool[i].set(new SoftReference<ByteBuffer>(buf));
- return;
+ boolean offerLast(ByteBuffer buf) {
+ if (count >= TEMP_BUF_POOL_SIZE) {
+ return false;
+ } else {
+ int next = (start + count) % TEMP_BUF_POOL_SIZE;
+ buffers[next] = buf;
+ count++;
+ return true;
}
}
- // release memory
- ((DirectBuffer)buf).cleaner().clean();
+ boolean isEmpty() {
+ return count == 0;
+ }
+
+ ByteBuffer removeFirst() {
+ assert count > 0;
+ ByteBuffer buf = buffers[start];
+ buffers[start] = null;
+ start = next(start);
+ count--;
+ return buf;
+ }
+ }
+
+ /**
+ * Returns a temporary buffer of at least the given size
+ */
+ static ByteBuffer getTemporaryDirectBuffer(int size) {
+ BufferCache cache = bufferCache.get();
+ ByteBuffer buf = cache.get(size);
+ if (buf != null) {
+ return buf;
+ } else {
+ // No suitable buffer in the cache so we need to allocate a new
+ // one. To avoid the cache growing then we remove the first
+ // buffer from the cache and free it.
+ if (!cache.isEmpty()) {
+ buf = cache.removeFirst();
+ free(buf);
+ }
+ return ByteBuffer.allocateDirect(size);
+ }
+ }
+
+ /**
+ * Releases a temporary buffer by returning to the cache or freeing it.
+ */
+ static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
+ offerFirstTemporaryDirectBuffer(buf);
+ }
+
+ /**
+ * 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) {
+ assert buf != null;
+ BufferCache cache = bufferCache.get();
+ if (!cache.offerFirst(buf)) {
+ // cache is full
+ free(buf);
+ }
+ }
+
+ /**
+ * Releases a temporary buffer by returning to the cache or freeing it. If
+ * 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) {
+ assert buf != null;
+ BufferCache cache = bufferCache.get();
+ if (!cache.offerLast(buf)) {
+ // cache is full
+ free(buf);
+ }
+ }
+
+ /**
+ * Frees the memory for the given direct buffer
+ */
+ private static void free(ByteBuffer buf) {
+ ((DirectBuffer)buf).cleaner().clean();
}
private static class SelectorWrapper {