001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.bytes;
006:
007: import com.tc.logging.LossyTCLogger;
008: import com.tc.logging.TCLogger;
009: import com.tc.logging.TCLogging;
010:
011: import java.util.LinkedList;
012:
013: /**
014: * TCByteBuffer source that hides JDK dependencies and that can pool instances. Instance pooling is likely to be a good
015: * idea for fixed size buffers and definitely a good idea for java 1.4 direct buffers (since their
016: * allocation/deallication is more expensive than regular java objects).
017: *
018: * @author teck
019: */
020: public class TCByteBufferFactory {
021: // 10485760 == 10MB
022: private static final int WARN_THRESHOLD = 10485760;
023:
024: private static final boolean disablePooling = false;
025:
026: private static final LinkedList directFreePool = new LinkedList();
027:
028: private static final LinkedList nonDirectFreePool = new LinkedList();
029:
030: // 4096 bytes * 2048 * 4 = 32 MB total
031: private static final int DEFAULT_FIXED_SIZE = 4096;
032: private static final int MAX_POOL_SIZE = 2048 * 4;
033:
034: // XXX: make me configurable (one time only, fixed sized buffers need to stay the same size!)
035: private static final int fixedBufferSize = DEFAULT_FIXED_SIZE;
036:
037: private static final TCByteBuffer[] EMPTY_BB_ARRAY = new TCByteBuffer[0];
038:
039: private static final TCLogger logger = TCLogging
040: .getLogger(TCByteBufferFactory.class);
041: private static final TCLogger lossyLogger = new LossyTCLogger(
042: logger);
043:
044: private static final TCByteBuffer ZERO_BYTE_BUFFER = TCByteBufferImpl
045: .wrap(new byte[0]);
046:
047: private static TCByteBuffer createNewInstance(boolean direct,
048: int capacity, int index, int totalCount) {
049: try {
050: TCByteBuffer rv = new TCByteBufferImpl(capacity, direct);
051: // Assert.assertEquals(0, rv.position());
052: // Assert.assertEquals(capacity, rv.capacity());
053: // Assert.assertEquals(capacity, rv.limit());
054: return rv;
055: } catch (OutOfMemoryError oome) {
056: // try to log some useful context. Most OOMEs don't have stack traces unfortunately
057: logger.error("OOME trying to allocate "
058: + (direct ? "direct" : "non-direct")
059: + " buffer of size " + capacity + " (index "
060: + index + " of count " + totalCount + ")");
061: throw oome;
062: }
063: }
064:
065: /**
066: * Get a single variable sized TCByteBuffer instance Note: These are not pooled (yet)
067: *
068: * @param size The desired minimum capacity of the buffer. The actual capacity may be higher. The buffer's limit will
069: * be equal to it's capacity.
070: * @param direct True to hint that the buffer should be a direct buffer (ie. not on the Java heap). A direct buffer
071: * will never be returned if this parameter is false. A direct buffer may or MAY NOT returned if the parameter
072: * is true TODO :: Make this the only interface and make it return fixed size buffer also make sure only
073: * fixedBufferSize gets to the pool back.
074: */
075: public static TCByteBuffer getInstance(final boolean direct,
076: int size) {
077:
078: if (size > WARN_THRESHOLD) {
079: logger.warn("Asking for a large amount of memory: " + size
080: + " bytes");
081: }
082: if (size < 0) {
083: throw new IllegalArgumentException(
084: "Requested length cannot be less than zero");
085: }
086: if (size == 0) {
087: return ZERO_BYTE_BUFFER;
088: }
089:
090: if (disablePooling || size > fixedBufferSize) {
091: return createNewInstance(direct, size);
092: } else {
093: return getFromPoolOrCreate(direct);
094: }
095: }
096:
097: private static TCByteBuffer getFromPoolOrCreate(final boolean direct) {
098: return getFromPoolOrCreate(direct, 0, 1);
099: }
100:
101: private static TCByteBuffer getFromPoolOrCreate(boolean direct,
102: int i, int numBuffers) {
103:
104: TCByteBuffer buffer = getFromPool(direct);
105: if (null == buffer) {
106: buffer = createNewInstance(direct, fixedBufferSize, i,
107: numBuffers);
108: }
109: return buffer;
110: }
111:
112: private static TCByteBuffer createNewInstance(boolean direct,
113: int bufferSize) {
114: return createNewInstance(direct, bufferSize, 0, 1);
115: }
116:
117: /**
118: * Get enough fixed sized TCByteBuffer instances to contain the given number of bytes
119: *
120: * @param direct True to hint that the buffers should be direct buffers (ie. not on the Java heap). Direct buffers
121: * will never be returned if this parameter is false. Direct buffers may or MAY NOT returned if the parameter
122: * is true. The returned buffers may be a mix of direct and non-direct
123: * @param length
124: * @return an array of TCByteBuffer instances (with length always >= 1). The limit of the last buffer may be less then
125: * it's capacity to adjust for the given length
126: */
127: public static TCByteBuffer[] getFixedSizedInstancesForLength(
128: final boolean direct, final int length) {
129: if (length > WARN_THRESHOLD) {
130: logger.warn("Asking for a large amount of memory: "
131: + length + " bytes");
132: }
133:
134: if (length < 0) {
135: throw new IllegalArgumentException(
136: "Requested length cannot be less than zero");
137: }
138:
139: if (length == 0) {
140: return EMPTY_BB_ARRAY;
141: }
142:
143: int numBuffers = length / fixedBufferSize;
144: if ((length % fixedBufferSize) != 0) {
145: numBuffers++;
146: }
147:
148: TCByteBuffer rv[] = new TCByteBuffer[numBuffers];
149:
150: if (disablePooling) {
151: for (int i = 0; i < numBuffers; i++) {
152: rv[i] = createNewInstance(direct, fixedBufferSize, i,
153: numBuffers);
154: }
155: } else { // do pooling logic
156: for (int i = 0; i < numBuffers; i++) {
157: rv[i] = getFromPoolOrCreate(direct, i, numBuffers);
158: }
159: }
160:
161: // adjust limit of last buffer returned
162: TCByteBuffer lastBuffer = rv[rv.length - 1];
163: lastBuffer.limit(lastBuffer.capacity()
164: - ((numBuffers * fixedBufferSize) - length));
165:
166: // ensureSpace(rv, length);
167:
168: return rv;
169: }
170:
171: private static TCByteBuffer getFromPool(boolean direct) {
172: if (disablePooling)
173: return null;
174: TCByteBuffer buf = null;
175: if (direct) {
176: synchronized (directFreePool) {
177: if (directFreePool.size() > 0) {
178: buf = (TCByteBuffer) directFreePool.removeFirst();
179: buf.checkedOut();
180: }
181: }
182: } else {
183: synchronized (nonDirectFreePool) {
184: if (nonDirectFreePool.size() > 0) {
185: buf = (TCByteBuffer) nonDirectFreePool
186: .removeFirst();
187: buf.checkedOut();
188: }
189: }
190: }
191: return buf;
192: }
193:
194: public static void returnBuffers(TCByteBuffer buffers[]) {
195: if (disablePooling) {
196: return;
197: }
198:
199: for (int i = 0; i < buffers.length; i++) {
200: TCByteBuffer buf = buffers[i];
201: returnBuffer(buf);
202: }
203: }
204:
205: public static void returnBuffer(TCByteBuffer buf) {
206: if (disablePooling) {
207: return;
208: }
209:
210: if (buf.capacity() == fixedBufferSize) {
211: if (buf.isDirect()) {
212: synchronized (directFreePool) {
213: buf.commit();
214: if (directFreePool.size() < MAX_POOL_SIZE) {
215: // buf.clear();
216: directFreePool.addLast(buf);
217: }
218: }
219: } else {
220: synchronized (nonDirectFreePool) {
221: buf.commit();
222: if (nonDirectFreePool.size() < MAX_POOL_SIZE) {
223: // buf.clear();
224: nonDirectFreePool.addLast(buf);
225: } else {
226: lossyLogger.info("MAX POOL Size of "
227: + nonDirectFreePool.size()
228: + " reached !");
229: }
230: }
231: }
232: }
233: }
234:
235: public static TCByteBuffer wrap(byte[] buf) {
236: return TCByteBufferImpl.wrap(buf);
237: }
238:
239: public static TCByteBuffer copyAndWrap(byte[] buf) {
240: TCByteBuffer rv = null;
241: if (buf != null) {
242: rv = getInstance(false, buf.length);
243: rv.put(buf).rewind();
244: } else {
245: rv = getInstance(false, 0);
246: }
247: return rv;
248: }
249:
250: }
|