jdk/src/java.base/share/classes/sun/nio/ch/Util.java
changeset 35386 a54f3c20e83d
parent 34882 ce2a8ec851c1
child 35641 da165fd9c886
--- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java	Thu Jan 28 12:55:19 2016 +0000
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java	Thu Jan 28 10:58:10 2016 -0500
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -44,6 +44,9 @@
     // 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>()
@@ -55,6 +58,52 @@
     };
 
     /**
+     * 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 {
@@ -80,6 +129,9 @@
          * 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
 
@@ -117,6 +169,9 @@
         }
 
         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 {
@@ -128,6 +183,9 @@
         }
 
         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 {
@@ -156,6 +214,15 @@
      * 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) {
@@ -185,6 +252,13 @@
      * 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)) {
@@ -200,6 +274,13 @@
      * 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)) {