package jane.test.net; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayDeque; /** * DirectByteBuffer的对象池 * <p> * 注意: 池比较大的情况下,需要指定direct内存池大小(默认64M): -XX:MaxDirectMemorySize=size */ public final class ByteBufferPool { private static final int DEFAULT_MAX_POOL_SIZE = 32; private static final int DEFAULT_MAX_CACHED_BUFFER_SIZE = 1 << 13; // 8KB // public static final AtomicInteger allocCount = new AtomicInteger(); // public static final AtomicInteger cacheCount = new AtomicInteger(); // public static final AtomicInteger offerCount = new AtomicInteger(); private static final ByteBufferPool _defPool = new ByteBufferPool(); private final int _maxPoolSize; private final int _maxCachedBufferSize; private final ThreadLocal<ArrayDeque<ByteBuffer>[]> _directBuffers = new TestThreadLocal(); private final class TestThreadLocal extends ThreadLocal<ArrayDeque<ByteBuffer>[]> { @Override protected ArrayDeque<ByteBuffer>[] initialValue() { @SuppressWarnings("unchecked") ArrayDeque<ByteBuffer>[] poolMap = new ArrayDeque[32]; poolMap[0] = new ArrayDeque<>(); for(int k = 1; k <= _maxCachedBufferSize; k += k) poolMap[getIdx(k)] = new ArrayDeque<>(); return poolMap; } } public static ByteBufferPool def() { return _defPool; } public ByteBufferPool() { this(DEFAULT_MAX_POOL_SIZE, DEFAULT_MAX_CACHED_BUFFER_SIZE); } public ByteBufferPool(int maxPoolSize, int maxCachedBufferSize) // maxCachedBufferSize must be 2^n { this._maxPoolSize = maxPoolSize; this._maxCachedBufferSize = maxCachedBufferSize; } private static int getIdx(int cap) // cap=2^n:[0,0x40000000] => [0,31] { return (int)((4719556544L * cap) >> 32) & 31; // minimal perfect hash function } public ByteBuffer allocateDirect(int capacity) { if(capacity < 0) capacity = 0; int actualCapacity = Integer.highestOneBit(capacity); if(actualCapacity < capacity) { actualCapacity += actualCapacity; if(actualCapacity < 0) actualCapacity = capacity; } ByteBuffer bb; if(actualCapacity <= _maxCachedBufferSize && (bb = _directBuffers.get()[getIdx(actualCapacity)].pollFirst()) != null) { bb.clear(); bb.order(ByteOrder.BIG_ENDIAN); // cacheCount.incrementAndGet(); } else { bb = ByteBuffer.allocateDirect(actualCapacity); // allocCount.incrementAndGet(); } bb.limit(capacity); return bb; } public void free(ByteBuffer bb) { if(bb == null || !bb.isDirect()) return; int actualCapacity = Integer.highestOneBit(bb.capacity()); if(actualCapacity > _maxCachedBufferSize) return; ArrayDeque<ByteBuffer> bufQueue = _directBuffers.get()[getIdx(actualCapacity)]; if(bufQueue.size() < _maxPoolSize) { bufQueue.addFirst(bb); // offerCount.incrementAndGet(); } } }