001    // ========================================================================
002    // $Id: ByteArrayPool.java,v 1.5 2004/12/01 21:00:24 hoschek3 Exp $
003    // Copyright 2002-2004 Mort Bay Consulting Pty. Ltd.
004    // ------------------------------------------------------------------------
005    // Licensed under the Apache License, Version 2.0 (the "License");
006    // you may not use this file except in compliance with the License.
007    // You may obtain a copy of the License at 
008    // http://www.apache.org/licenses/LICENSE-2.0
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    // ========================================================================
015    
016    package gov.lbl.dsd.sea.nio.util;
017    
018    /**
019     * Simple fixed-size thread-safe pool for recycling byte arrays. Each thread has
020     * its own local pool, and each pool can hold at most a given number of byte
021     * arrays, evicting arrays beyond that point, as configured through a system
022     * property.
023     * <p>
024     * Completely unsynchronized, yet thread-safe implementation.
025     * <p>
026     * Simple, but often effective if an application creates medium to large
027     * temporary arrays at very high frequency, as is often the case in safe and
028     * clean I/O buffer management for network servers.
029     * <p>
030     * This is not a general purpose replacement for Javas built-in memory and
031     * garbage collection subsystem! Do not abuse this class for small or infrequent
032     * arrays - those are handled better (read: near-optimal) by the generational
033     * garbage collectors and memory allocators of current VMs.
034     * 
035     * @version $Id: ByteArrayPool.java,v 1.5 2004/12/01 21:00:24 hoschek3 Exp $
036     * @author Greg Wilkins (gregw)
037     * @author whoschek@lbl.gov (local improvements to apache-licensed jetty code)
038     */
039    public final class ByteArrayPool {
040    
041            private static final int MAX_POOL_ENTRIES = Integer.getInteger(
042                            ByteArrayPool.class.getName() + ".maxPoolEntries", 8).intValue();
043            
044            private static final int MIN_ARRAY_SIZE = Integer.getInteger(
045                            ByteArrayPool.class.getName() + ".minArraySize", 512 + 1).intValue();
046            
047            private static final int MAX_ARRAY_SIZE = Integer.getInteger(
048                            ByteArrayPool.class.getName() + ".maxArraySize", Integer.MAX_VALUE).intValue();
049            
050            private static final ThreadLocal localPool = new ThreadLocalPool();
051            private static int slot;
052    
053            private static final boolean debug = false; // hotspot vm removes dead code if false
054            
055            private ByteArrayPool() {} // not instantiable
056    
057            /**
058             * Returns a byte array that has exactly the given size. If there is no such
059             * array in the pool, then creates and returns a new array with the given
060             * size.
061             * 
062             * @param size
063             *            the desired size of the byte array.
064             * @return Byte array of desired size.
065             */
066            public static byte[] takeExactly(int size) {
067                    if (size >= MIN_ARRAY_SIZE && size <= MAX_ARRAY_SIZE) {
068                            byte[][] pool = (byte[][]) localPool.get();
069                            boolean full = true;
070                            for (int i = pool.length; i-- > 0;) {
071                                    if (pool[i] != null && pool[i].length == size) {
072                                            byte[] bytes = pool[i];
073                                            pool[i] = null;
074                                            if (debug) memset(bytes, (byte) 0);
075                                            return bytes;
076                                    } else
077                                            full = false;
078                            }
079            
080                            if (full) for (int i = pool.length; i-- > 0;)
081                                    pool[i] = null;
082                    }
083    
084                    return new byte[size];
085            }
086    
087            /**
088             * Returns a byte array that has at least the given size. If there is no
089             * such array in the pool, then creates and returns a new array with the
090             * given minSize.
091             * 
092             * @param minSize
093             *            the minimum desired size of the byte array.
094             * @return Byte array of desired size.
095             */
096            public static byte[] take(int minSize) {
097                    if (minSize >= MIN_ARRAY_SIZE && minSize <= MAX_ARRAY_SIZE) {
098                            byte[][] pool = (byte[][]) localPool.get();
099                            for (int i = pool.length; i-- > 0;) {
100                                    if (pool[i] != null && pool[i].length >= minSize) {
101                                            byte[] bytes = pool[i];
102                                            pool[i] = null;
103                                            if (debug) memset(bytes, (byte) 0);
104                                            return bytes;
105                                    }
106                            }
107                    }
108    
109                    return new byte[minSize];
110            }
111    
112            /**
113             * Recycles the given byte array back to the pool.
114             * 
115             * @param bytes the array to recycle
116             */
117            public static void put(final byte[] bytes) {
118                    if (bytes == null || bytes.length < MIN_ARRAY_SIZE || bytes.length > MAX_ARRAY_SIZE) return;
119    
120                    if (debug) memset(bytes, (byte) 0);
121                    final byte[][] pool = (byte[][]) localPool.get();
122                    for (int i = pool.length; i-- > 0;) {
123                            if (pool[i] == null) {
124                                    pool[i] = bytes;
125                                    return;
126                            }
127                    }
128                    putEvict(bytes, pool);
129            }
130            
131            private static void putEvict(final byte[] bytes, final byte[][] pool) {
132                    // int load is guaranteed to be atomic by VM spec.
133                    // plus, we handle races on store safely...
134                    int s = slot++;
135                    if (s < 0) s = -s;
136                    pool[s % pool.length] = bytes;
137            }
138            
139            /**
140             * Sets all elements of the given array to the given value.
141             * 
142             * @param bytes the array to initialize
143             */
144            private static void memset(final byte[] bytes, final byte value) {
145                    for (int i = bytes.length; --i >= 0; ) bytes[i] = value;
146            }
147    
148            /* ------------------------------------------------------------ */
149            private static final class ThreadLocalPool extends ThreadLocal {
150    
151                    protected Object initialValue() {
152                            return new byte[MAX_POOL_ENTRIES][];
153                    }
154            }
155    }