KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > bytes > TCByteBufferFactory


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.bytes;
5
6 import com.tc.exception.TCRuntimeException;
7 import com.tc.logging.LossyTCLogger;
8 import com.tc.logging.TCLogger;
9 import com.tc.logging.TCLogging;
10 import com.tc.util.runtime.IOFlavor;
11
12 import java.util.LinkedList JavaDoc;
13
14 /**
15  * TCByteBuffer source that hides JDK dependencies and that can pool instances. Instance pooling is likely to be a good
16  * idea for fixed size buffers and definitely a good idea for java 1.4 direct buffers (since their
17  * allocation/deallication is more expensive than regular java objects).
18  *
19  * @author teck
20  */

21 public class TCByteBufferFactory {
22   // 10485760 == 10MB
23
private static final int WARN_THRESHOLD = 10485760;
24
25   private static final boolean disablePooling = false;
26
27   private static final TCByteBufferFactoryIF factory;
28
29   private static final LinkedList JavaDoc directFreePool = new LinkedList JavaDoc();
30
31   private static final LinkedList JavaDoc nonDirectFreePool = new LinkedList JavaDoc();
32
33   // 4096 bytes * 2048 * 4 = 32 MB total
34
private static final int DEFAULT_FIXED_SIZE = 4096;
35   private static final int MAX_POOL_SIZE = 2048 * 4;
36
37   // XXX: make me configurable (one time only, fixed sized buffers need to stay the same size!)
38
private static final int fixedBufferSize = DEFAULT_FIXED_SIZE;
39
40   private static final boolean useNIO;
41
42   private static final TCByteBuffer[] EMPTY_BB_ARRAY = new TCByteBuffer[] {};
43
44   private static final TCLogger logger = TCLogging.getLogger(TCByteBufferFactory.class);
45   private static final TCLogger lossyLogger = new LossyTCLogger(logger);
46
47   private static final TCByteBuffer ZERO_BYTE_BUFFER;
48
49   static {
50     Class JavaDoc factoryClass = null;
51     useNIO = IOFlavor.isNioAvailable();
52
53     try {
54       if (useNIO) {
55         factoryClass = Class.forName("com.tc.bytes.TCByteBufferFactoryJDK14");
56       } else {
57         factoryClass = Class.forName("com.tc.bytes.TCByteBufferFactoryJDK13");
58       }
59     } catch (ClassNotFoundException JavaDoc cfe) {
60       throw new TCRuntimeException("internal error", cfe);
61     }
62
63     try {
64       factory = (TCByteBufferFactoryIF) factoryClass.newInstance();
65     } catch (InstantiationException JavaDoc ie) {
66       throw new TCRuntimeException("internal error", ie);
67     } catch (IllegalAccessException JavaDoc iae) {
68       throw new TCRuntimeException("internal error", iae);
69     }
70     ZERO_BYTE_BUFFER = factory.wrap(new byte[0]);
71   }
72
73   private static TCByteBuffer createNewInstance(boolean direct, int capacity, int index, int totalCount) {
74     try {
75       TCByteBuffer rv = factory.getInstance(capacity, direct);
76       // Assert.assertEquals(0, rv.position());
77
// Assert.assertEquals(capacity, rv.capacity());
78
// Assert.assertEquals(capacity, rv.limit());
79
return rv;
80     } catch (OutOfMemoryError JavaDoc oome) {
81       // try to log some useful context. Most OOMEs don't have stack traces unfortunately
82
logger.error("OOME trying to allocate " + (direct ? "direct" : "non-direct") + " buffer of size " + capacity
83                    + " (index " + index + " of count " + totalCount + ")");
84       throw oome;
85     }
86   }
87
88   /**
89    * Get a single variable sized TCByteBuffer instance Note: These are not pooled (yet)
90    *
91    * @param size The desired minimum capacity of the buffer. The actual capacity may be higher. The buffer's limit will
92    * be equal to it's capacity.
93    * @param direct True to hint that the buffer should be a direct buffer (ie. not on the Java heap). A direct buffer
94    * will never be returned if this parameter is false. A direct buffer may or MAY NOT returned if the parameter
95    * is true TODO :: Make this the only interface and make it return fixed size buffer also make sure only
96    * fixedBufferSize gets to the pool back.
97    */

98   public static TCByteBuffer getInstance(final boolean direct, int size) {
99
100     if (size > WARN_THRESHOLD) {
101       logger.warn("Asking for a large amount of memory: " + size + " bytes");
102     }
103     if (size < 0) { throw new IllegalArgumentException JavaDoc("Requested length cannot be less than zero"); }
104     if(size == 0) { return ZERO_BYTE_BUFFER; }
105
106     if (disablePooling || size > fixedBufferSize) {
107       return createNewInstance(direct, size);
108     } else {
109       return getFromPoolOrCreate(direct);
110     }
111   }
112
113   private static TCByteBuffer getFromPoolOrCreate(final boolean direct) {
114     return getFromPoolOrCreate(direct, 0, 1);
115   }
116
117   private static TCByteBuffer getFromPoolOrCreate(boolean direct, int i, int numBuffers) {
118
119     TCByteBuffer buffer = getFromPool(direct);
120     if (null == buffer) {
121       buffer = createNewInstance(direct, fixedBufferSize, i, numBuffers);
122     }
123     return buffer;
124   }
125
126   private static TCByteBuffer createNewInstance(boolean direct, int bufferSize) {
127     return createNewInstance(direct, bufferSize, 0, 1);
128   }
129
130   /**
131    * Get enough fixed sized TCByteBuffer instances to contain the given number of bytes
132    *
133    * @param direct True to hint that the buffers should be direct buffers (ie. not on the Java heap). Direct buffers
134    * will never be returned if this parameter is false. Direct buffers may or MAY NOT returned if the parameter
135    * is true. The returned buffers may be a mix of direct and non-direct
136    * @param length
137    * @return an array of TCByteBuffer instances (with length always >= 1). The limit of the last buffer may be less then
138    * it's capacity to adjust for the given length
139    */

140   public static TCByteBuffer[] getFixedSizedInstancesForLength(final boolean direct, final int length) {
141     if (length > WARN_THRESHOLD) {
142       logger.warn("Asking for a large amount of memory: " + length + " bytes");
143     }
144
145     if (length < 0) { throw new IllegalArgumentException JavaDoc("Requested length cannot be less than zero"); }
146
147     if (length == 0) { return EMPTY_BB_ARRAY; }
148
149     int numBuffers = length / fixedBufferSize;
150     if ((length % fixedBufferSize) != 0) {
151       numBuffers++;
152     }
153
154     TCByteBuffer rv[] = new TCByteBuffer[numBuffers];
155
156     if (disablePooling) {
157       for (int i = 0; i < numBuffers; i++) {
158         rv[i] = createNewInstance(direct, fixedBufferSize, i, numBuffers);
159       }
160     } else { // do pooling logic
161
for (int i = 0; i < numBuffers; i++) {
162         rv[i] = getFromPoolOrCreate(direct, i, numBuffers);
163       }
164     }
165
166     // adjust limit of last buffer returned
167
TCByteBuffer lastBuffer = rv[rv.length - 1];
168     lastBuffer.limit(lastBuffer.capacity() - ((numBuffers * fixedBufferSize) - length));
169
170     // ensureSpace(rv, length);
171

172     return rv;
173   }
174
175   private static TCByteBuffer getFromPool(boolean direct) {
176     if (disablePooling) return null;
177     if (direct) {
178       synchronized (directFreePool) {
179         return directFreePool.size() > 0 ? (TCByteBuffer) directFreePool.removeFirst() : null;
180       }
181     } else {
182       synchronized (nonDirectFreePool) {
183         return nonDirectFreePool.size() > 0 ? (TCByteBuffer) nonDirectFreePool.removeFirst() : null;
184       }
185     }
186   }
187
188   public static void returnBuffers(TCByteBuffer buffers[]) {
189     if (disablePooling) { return; }
190
191     for (int i = 0; i < buffers.length; i++) {
192       TCByteBuffer buf = buffers[i];
193       returnBuffer(buf);
194     }
195   }
196
197   public static void returnBuffer(TCByteBuffer buf) {
198     if (disablePooling) { return; }
199
200     if (buf.capacity() == fixedBufferSize) {
201       if (buf.isDirect()) {
202         synchronized (directFreePool) {
203           if (directFreePool.size() < MAX_POOL_SIZE) {
204 // buf.clear();
205
directFreePool.addLast(buf);
206           }
207         }
208       } else {
209         synchronized (nonDirectFreePool) {
210           if (nonDirectFreePool.size() < MAX_POOL_SIZE) {
211 // buf.clear();
212
nonDirectFreePool.addLast(buf);
213           }
214           else {
215             lossyLogger.info("MAX POOL Size of " + nonDirectFreePool.size() + " reached !");
216           }
217         }
218       }
219     }
220   }
221
222   public static TCByteBuffer wrap(byte[] buf) {
223     return factory.wrap(buf);
224   }
225
226   public static TCByteBuffer copyAndWrap(byte[] buf) {
227     TCByteBuffer rv = null;
228     if (buf != null) {
229       rv = getInstance(false, buf.length);
230       rv.put(buf).rewind();
231     } else {
232       rv = getInstance(false, 0);
233     }
234     return rv;
235   }
236
237 }
Popular Tags