< 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 /*
   2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  30 import java.io.IOException;
  31 import java.io.FileDescriptor;
  32 import java.nio.ByteBuffer;
  33 import java.nio.MappedByteBuffer;
  34 import java.nio.channels.*;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 import java.util.*;
  38 import sun.misc.Unsafe;
  39 import sun.misc.Cleaner;
  40 import sun.security.action.GetPropertyAction;
  41 
  42 
  43 public class Util {
  44 
  45     // -- Caches --
  46 
  47     // The number of temp buffers in our pool
  48     private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX;
  49 



  50     // Per-thread cache of temporary direct buffers
  51     private static ThreadLocal<BufferCache> bufferCache =
  52         new ThreadLocal<BufferCache>()
  53     {
  54         @Override
  55         protected BufferCache initialValue() {
  56             return new BufferCache();
  57         }
  58     };
  59 
  60     /**














































  61      * A simple cache of direct buffers.
  62      */
  63     private static class BufferCache {
  64         // the array of buffers
  65         private ByteBuffer[] buffers;
  66 
  67         // the number of buffers in the cache
  68         private int count;
  69 
  70         // the index of the first valid buffer (undefined if count == 0)
  71         private int start;
  72 
  73         private int next(int i) {
  74             return (i + 1) % TEMP_BUF_POOL_SIZE;
  75         }
  76 
  77         BufferCache() {
  78             buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];
  79         }
  80 
  81         /**
  82          * Removes and returns a buffer from the cache of at least the given
  83          * size (or null if no suitable buffer is found).
  84          */
  85         ByteBuffer get(int size) {



  86             if (count == 0)
  87                 return null;  // cache is empty
  88 
  89             ByteBuffer[] buffers = this.buffers;
  90 
  91             // search for suitable buffer (often the first buffer will do)
  92             ByteBuffer buf = buffers[start];
  93             if (buf.capacity() < size) {
  94                 buf = null;
  95                 int i = start;
  96                 while ((i = next(i)) != start) {
  97                     ByteBuffer bb = buffers[i];
  98                     if (bb == null)
  99                         break;
 100                     if (bb.capacity() >= size) {
 101                         buf = bb;
 102                         break;
 103                     }
 104                 }
 105                 if (buf == null)
 106                     return null;
 107                 // move first element to here to avoid re-packing
 108                 buffers[i] = buffers[start];
 109             }
 110 
 111             // remove first element
 112             buffers[start] = null;
 113             start = next(start);
 114             count--;
 115 
 116             // prepare the buffer and return it
 117             buf.rewind();
 118             buf.limit(size);
 119             return buf;
 120         }
 121 
 122         boolean offerFirst(ByteBuffer buf) {



 123             if (count >= TEMP_BUF_POOL_SIZE) {
 124                 return false;
 125             } else {
 126                 start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;
 127                 buffers[start] = buf;
 128                 count++;
 129                 return true;
 130             }
 131         }
 132 
 133         boolean offerLast(ByteBuffer buf) {



 134             if (count >= TEMP_BUF_POOL_SIZE) {
 135                 return false;
 136             } else {
 137                 int next = (start + count) % TEMP_BUF_POOL_SIZE;
 138                 buffers[next] = buf;
 139                 count++;
 140                 return true;
 141             }
 142         }
 143 
 144         boolean isEmpty() {
 145             return count == 0;
 146         }
 147 
 148         ByteBuffer removeFirst() {
 149             assert count > 0;
 150             ByteBuffer buf = buffers[start];
 151             buffers[start] = null;
 152             start = next(start);
 153             count--;
 154             return buf;
 155         }
 156     }
 157 
 158     /**
 159      * Returns a temporary buffer of at least the given size
 160      */
 161     public static ByteBuffer getTemporaryDirectBuffer(int size) {









 162         BufferCache cache = bufferCache.get();
 163         ByteBuffer buf = cache.get(size);
 164         if (buf != null) {
 165             return buf;
 166         } else {
 167             // No suitable buffer in the cache so we need to allocate a new
 168             // one. To avoid the cache growing then we remove the first
 169             // buffer from the cache and free it.
 170             if (!cache.isEmpty()) {
 171                 buf = cache.removeFirst();
 172                 free(buf);
 173             }
 174             return ByteBuffer.allocateDirect(size);
 175         }
 176     }
 177 
 178     /**
 179      * Releases a temporary buffer by returning to the cache or freeing it.
 180      */
 181     public static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
 182         offerFirstTemporaryDirectBuffer(buf);
 183     }
 184 
 185     /**
 186      * Releases a temporary buffer by returning to the cache or freeing it. If
 187      * returning to the cache then insert it at the start so that it is
 188      * likely to be returned by a subsequent call to getTemporaryDirectBuffer.
 189      */
 190     static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {







 191         assert buf != null;
 192         BufferCache cache = bufferCache.get();
 193         if (!cache.offerFirst(buf)) {
 194             // cache is full
 195             free(buf);
 196         }
 197     }
 198 
 199     /**
 200      * Releases a temporary buffer by returning to the cache or freeing it. If
 201      * returning to the cache then insert it at the end. This makes it
 202      * suitable for scatter/gather operations where the buffers are returned to
 203      * cache in same order that they were obtained.
 204      */
 205     static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {







 206         assert buf != null;
 207         BufferCache cache = bufferCache.get();
 208         if (!cache.offerLast(buf)) {
 209             // cache is full
 210             free(buf);
 211         }
 212     }
 213 
 214     /**
 215      * Frees the memory for the given direct buffer
 216      */
 217     private static void free(ByteBuffer buf) {
 218         ((DirectBuffer)buf).cleaner().clean();
 219     }
 220 
 221 
 222     // -- Random stuff --
 223 
 224     static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {
 225         if ((offset == 0) && (length == bs.length))


   1 /*
   2  * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  30 import java.io.IOException;
  31 import java.io.FileDescriptor;
  32 import java.nio.ByteBuffer;
  33 import java.nio.MappedByteBuffer;
  34 import java.nio.channels.*;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 import java.util.*;
  38 import sun.misc.Unsafe;
  39 import sun.misc.Cleaner;
  40 import sun.security.action.GetPropertyAction;
  41 
  42 
  43 public class Util {
  44 
  45     // -- Caches --
  46 
  47     // The number of temp buffers in our pool
  48     private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX;
  49 
  50     // The max size allowed for a cached temp buffer, in bytes
  51     private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize();
  52 
  53     // Per-thread cache of temporary direct buffers
  54     private static ThreadLocal<BufferCache> bufferCache =
  55         new ThreadLocal<BufferCache>()
  56     {
  57         @Override
  58         protected BufferCache initialValue() {
  59             return new BufferCache();
  60         }
  61     };
  62 
  63     /**
  64      * Returns the max size allowed for a cached temp buffers, in
  65      * bytes. It defaults to Long.MAX_VALUE. It can be set with the
  66      * jdk.nio.maxCachedBufferSize property. Even though
  67      * ByteBuffer.capacity() returns an int, we're using a long here
  68      * for potential future-proofing.
  69      */
  70     private static long getMaxCachedBufferSize() {
  71         String s = java.security.AccessController.doPrivileged(
  72             new PrivilegedAction<String>() {
  73                 @Override
  74                 public String run() {
  75                     return System.getProperty("jdk.nio.maxCachedBufferSize");
  76                 }
  77             });
  78         if (s != null) {
  79             try {
  80                 long m = Long.parseLong(s);
  81                 if (m >= 0) {
  82                     return m;
  83                 } else {
  84                     // if it's negative, ignore the system property
  85                 }
  86             } catch (NumberFormatException e) {
  87                 // if the string is not well formed, ignore the system property
  88             }
  89         }
  90         return Long.MAX_VALUE;
  91     }
  92 
  93     /**
  94      * Returns true if a buffer of this size is too large to be
  95      * added to the buffer cache, false otherwise.
  96      */
  97     private static boolean isBufferTooLarge(int size) {
  98         return size > MAX_CACHED_BUFFER_SIZE;
  99     }
 100 
 101     /**
 102      * Returns true if the buffer is too large to be added to the
 103      * buffer cache, false otherwise.
 104      */
 105     private static boolean isBufferTooLarge(ByteBuffer buf) {
 106         return isBufferTooLarge(buf.capacity());
 107     }
 108 
 109     /**
 110      * A simple cache of direct buffers.
 111      */
 112     private static class BufferCache {
 113         // the array of buffers
 114         private ByteBuffer[] buffers;
 115 
 116         // the number of buffers in the cache
 117         private int count;
 118 
 119         // the index of the first valid buffer (undefined if count == 0)
 120         private int start;
 121 
 122         private int next(int i) {
 123             return (i + 1) % TEMP_BUF_POOL_SIZE;
 124         }
 125 
 126         BufferCache() {
 127             buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];
 128         }
 129 
 130         /**
 131          * Removes and returns a buffer from the cache of at least the given
 132          * size (or null if no suitable buffer is found).
 133          */
 134         ByteBuffer get(int size) {
 135             // Don't call this if the buffer would be too large.
 136             assert !isBufferTooLarge(size);
 137 
 138             if (count == 0)
 139                 return null;  // cache is empty
 140 
 141             ByteBuffer[] buffers = this.buffers;
 142 
 143             // search for suitable buffer (often the first buffer will do)
 144             ByteBuffer buf = buffers[start];
 145             if (buf.capacity() < size) {
 146                 buf = null;
 147                 int i = start;
 148                 while ((i = next(i)) != start) {
 149                     ByteBuffer bb = buffers[i];
 150                     if (bb == null)
 151                         break;
 152                     if (bb.capacity() >= size) {
 153                         buf = bb;
 154                         break;
 155                     }
 156                 }
 157                 if (buf == null)
 158                     return null;
 159                 // move first element to here to avoid re-packing
 160                 buffers[i] = buffers[start];
 161             }
 162 
 163             // remove first element
 164             buffers[start] = null;
 165             start = next(start);
 166             count--;
 167 
 168             // prepare the buffer and return it
 169             buf.rewind();
 170             buf.limit(size);
 171             return buf;
 172         }
 173 
 174         boolean offerFirst(ByteBuffer buf) {
 175             // Don't call this if the buffer is too large.
 176             assert !isBufferTooLarge(buf);
 177 
 178             if (count >= TEMP_BUF_POOL_SIZE) {
 179                 return false;
 180             } else {
 181                 start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;
 182                 buffers[start] = buf;
 183                 count++;
 184                 return true;
 185             }
 186         }
 187 
 188         boolean offerLast(ByteBuffer buf) {
 189             // Don't call this if the buffer is too large.
 190             assert !isBufferTooLarge(buf);
 191 
 192             if (count >= TEMP_BUF_POOL_SIZE) {
 193                 return false;
 194             } else {
 195                 int next = (start + count) % TEMP_BUF_POOL_SIZE;
 196                 buffers[next] = buf;
 197                 count++;
 198                 return true;
 199             }
 200         }
 201 
 202         boolean isEmpty() {
 203             return count == 0;
 204         }
 205 
 206         ByteBuffer removeFirst() {
 207             assert count > 0;
 208             ByteBuffer buf = buffers[start];
 209             buffers[start] = null;
 210             start = next(start);
 211             count--;
 212             return buf;
 213         }
 214     }
 215 
 216     /**
 217      * Returns a temporary buffer of at least the given size
 218      */
 219     public static ByteBuffer getTemporaryDirectBuffer(int size) {
 220         // If a buffer of this size is too large for the cache, there
 221         // should not be a buffer in the cache that is at least as
 222         // large. So we'll just create a new one. Also, we don't have
 223         // to remove the buffer from the cache (as this method does
 224         // below) given that we won't put the new buffer in the cache.
 225         if (isBufferTooLarge(size)) {
 226             return ByteBuffer.allocateDirect(size);
 227         }
 228 
 229         BufferCache cache = bufferCache.get();
 230         ByteBuffer buf = cache.get(size);
 231         if (buf != null) {
 232             return buf;
 233         } else {
 234             // No suitable buffer in the cache so we need to allocate a new
 235             // one. To avoid the cache growing then we remove the first
 236             // buffer from the cache and free it.
 237             if (!cache.isEmpty()) {
 238                 buf = cache.removeFirst();
 239                 free(buf);
 240             }
 241             return ByteBuffer.allocateDirect(size);
 242         }
 243     }
 244 
 245     /**
 246      * Releases a temporary buffer by returning to the cache or freeing it.
 247      */
 248     public static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
 249         offerFirstTemporaryDirectBuffer(buf);
 250     }
 251 
 252     /**
 253      * Releases a temporary buffer by returning to the cache or freeing it. If
 254      * returning to the cache then insert it at the start so that it is
 255      * likely to be returned by a subsequent call to getTemporaryDirectBuffer.
 256      */
 257     static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {
 258         // If the buffer is too large for the cache we don't have to
 259         // check the cache. We'll just free it.
 260         if (isBufferTooLarge(buf)) {
 261             free(buf);
 262             return;
 263         }
 264 
 265         assert buf != null;
 266         BufferCache cache = bufferCache.get();
 267         if (!cache.offerFirst(buf)) {
 268             // cache is full
 269             free(buf);
 270         }
 271     }
 272 
 273     /**
 274      * Releases a temporary buffer by returning to the cache or freeing it. If
 275      * returning to the cache then insert it at the end. This makes it
 276      * suitable for scatter/gather operations where the buffers are returned to
 277      * cache in same order that they were obtained.
 278      */
 279     static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {
 280         // If the buffer is too large for the cache we don't have to
 281         // check the cache. We'll just free it.
 282         if (isBufferTooLarge(buf)) {
 283             free(buf);
 284             return;
 285         }
 286 
 287         assert buf != null;
 288         BufferCache cache = bufferCache.get();
 289         if (!cache.offerLast(buf)) {
 290             // cache is full
 291             free(buf);
 292         }
 293     }
 294 
 295     /**
 296      * Frees the memory for the given direct buffer
 297      */
 298     private static void free(ByteBuffer buf) {
 299         ((DirectBuffer)buf).cleaner().clean();
 300     }
 301 
 302 
 303     // -- Random stuff --
 304 
 305     static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {
 306         if ((offset == 0) && (length == bs.length))


< prev index next >