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 }