2012-12-05 22:11:48 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2012 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package io.netty.buffer;
|
|
|
|
|
2014-04-09 11:07:14 +02:00
|
|
|
import io.netty.util.concurrent.GlobalEventExecutor;
|
|
|
|
import io.netty.util.concurrent.ScheduledFuture;
|
2013-03-05 09:55:24 +01:00
|
|
|
import io.netty.util.internal.PlatformDependent;
|
2013-04-03 05:08:01 +02:00
|
|
|
import io.netty.util.internal.SystemPropertyUtil;
|
|
|
|
import io.netty.util.internal.logging.InternalLogger;
|
|
|
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
2012-12-05 22:11:48 +01:00
|
|
|
|
|
|
|
import java.nio.ByteBuffer;
|
2014-04-09 11:07:14 +02:00
|
|
|
import java.util.IdentityHashMap;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
2012-12-05 22:11:48 +01:00
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
|
|
public class PooledByteBufAllocator extends AbstractByteBufAllocator {
|
|
|
|
|
2013-04-03 05:08:01 +02:00
|
|
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PooledByteBufAllocator.class);
|
|
|
|
|
2013-06-14 05:14:45 +02:00
|
|
|
private static final int DEFAULT_NUM_HEAP_ARENA;
|
|
|
|
private static final int DEFAULT_NUM_DIRECT_ARENA;
|
|
|
|
|
2013-04-03 05:08:01 +02:00
|
|
|
private static final int DEFAULT_PAGE_SIZE;
|
|
|
|
private static final int DEFAULT_MAX_ORDER; // 8192 << 11 = 16 MiB per chunk
|
2014-03-01 15:47:03 +01:00
|
|
|
private static final int DEFAULT_TINY_CACHE_SIZE;
|
|
|
|
private static final int DEFAULT_SMALL_CACHE_SIZE;
|
|
|
|
private static final int DEFAULT_NORMAL_CACHE_SIZE;
|
|
|
|
private static final int DEFAULT_MAX_CACHED_BUFFER_CAPACITY;
|
|
|
|
private static final int DEFAULT_CACHE_TRIM_INTERVAL;
|
2014-04-09 11:07:14 +02:00
|
|
|
private static final long DEFAULT_CACHE_CLEANUP_INTERVAL;
|
2012-12-05 22:11:48 +01:00
|
|
|
|
|
|
|
private static final int MIN_PAGE_SIZE = 4096;
|
|
|
|
private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2);
|
|
|
|
|
2013-04-03 05:08:01 +02:00
|
|
|
static {
|
|
|
|
int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192);
|
|
|
|
Throwable pageSizeFallbackCause = null;
|
|
|
|
try {
|
|
|
|
validateAndCalculatePageShifts(defaultPageSize);
|
|
|
|
} catch (Throwable t) {
|
|
|
|
pageSizeFallbackCause = t;
|
|
|
|
defaultPageSize = 8192;
|
|
|
|
}
|
|
|
|
DEFAULT_PAGE_SIZE = defaultPageSize;
|
|
|
|
|
|
|
|
int defaultMaxOrder = SystemPropertyUtil.getInt("io.netty.allocator.maxOrder", 11);
|
|
|
|
Throwable maxOrderFallbackCause = null;
|
|
|
|
try {
|
|
|
|
validateAndCalculateChunkSize(DEFAULT_PAGE_SIZE, defaultMaxOrder);
|
|
|
|
} catch (Throwable t) {
|
|
|
|
maxOrderFallbackCause = t;
|
|
|
|
defaultMaxOrder = 11;
|
|
|
|
}
|
|
|
|
DEFAULT_MAX_ORDER = defaultMaxOrder;
|
|
|
|
|
2013-06-14 05:14:45 +02:00
|
|
|
// Determine reasonable default for nHeapArena and nDirectArena.
|
|
|
|
// Assuming each arena has 3 chunks, the pool should not consume more than 50% of max memory.
|
|
|
|
final Runtime runtime = Runtime.getRuntime();
|
|
|
|
final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;
|
|
|
|
DEFAULT_NUM_HEAP_ARENA = Math.max(0,
|
|
|
|
SystemPropertyUtil.getInt(
|
|
|
|
"io.netty.allocator.numHeapArenas",
|
|
|
|
(int) Math.min(
|
|
|
|
runtime.availableProcessors(),
|
|
|
|
Runtime.getRuntime().maxMemory() / defaultChunkSize / 2 / 3)));
|
|
|
|
DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
|
|
|
|
SystemPropertyUtil.getInt(
|
|
|
|
"io.netty.allocator.numDirectArenas",
|
|
|
|
(int) Math.min(
|
|
|
|
runtime.availableProcessors(),
|
|
|
|
PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));
|
|
|
|
|
2014-03-01 15:47:03 +01:00
|
|
|
// cache sizes
|
|
|
|
DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);
|
|
|
|
DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
|
|
|
|
DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);
|
|
|
|
|
|
|
|
// 32 kb is the default maximum capacity of the cached buffer. Similar to what is explained in
|
|
|
|
// 'Scalable memory allocation using jemalloc'
|
|
|
|
DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
|
|
|
|
"io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
|
|
|
|
|
|
|
|
// the number of threshold of allocations when cached entries will be freed up if not frequently used
|
|
|
|
DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt(
|
|
|
|
"io.netty.allocator.cacheTrimInterval", 8192);
|
|
|
|
|
2014-04-09 11:07:14 +02:00
|
|
|
// the default interval at which we check for caches that are assigned to Threads that are not alive anymore
|
|
|
|
DEFAULT_CACHE_CLEANUP_INTERVAL = SystemPropertyUtil.getLong(
|
|
|
|
"io.netty.allocator.cacheCleanupInterval", 5000);
|
2013-04-03 05:08:01 +02:00
|
|
|
if (logger.isDebugEnabled()) {
|
2013-07-02 02:23:29 +02:00
|
|
|
logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA);
|
|
|
|
logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);
|
2013-04-03 05:08:01 +02:00
|
|
|
if (pageSizeFallbackCause == null) {
|
2013-07-02 02:23:29 +02:00
|
|
|
logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE);
|
2013-04-03 05:08:01 +02:00
|
|
|
} else {
|
2013-07-02 02:23:29 +02:00
|
|
|
logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause);
|
2013-04-03 05:08:01 +02:00
|
|
|
}
|
|
|
|
if (maxOrderFallbackCause == null) {
|
2013-07-02 02:23:29 +02:00
|
|
|
logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER);
|
2013-04-03 05:08:01 +02:00
|
|
|
} else {
|
2013-07-02 02:23:29 +02:00
|
|
|
logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause);
|
2013-04-03 05:08:01 +02:00
|
|
|
}
|
2013-07-02 02:23:29 +02:00
|
|
|
logger.debug("-Dio.netty.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER);
|
2014-03-01 15:47:03 +01:00
|
|
|
logger.debug("-Dio.netty.allocator.tinyCacheSize: {}", DEFAULT_TINY_CACHE_SIZE);
|
|
|
|
logger.debug("-Dio.netty.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE);
|
|
|
|
logger.debug("-Dio.netty.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE);
|
|
|
|
logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY);
|
|
|
|
logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}",
|
|
|
|
DEFAULT_CACHE_TRIM_INTERVAL);
|
2014-04-09 11:07:14 +02:00
|
|
|
logger.debug("-Dio.netty.allocator.cacheCleanupInterval: {} ms",
|
|
|
|
DEFAULT_CACHE_CLEANUP_INTERVAL);
|
2013-04-03 05:08:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 09:55:24 +01:00
|
|
|
public static final PooledByteBufAllocator DEFAULT =
|
|
|
|
new PooledByteBufAllocator(PlatformDependent.directBufferPreferred());
|
2012-12-05 22:11:48 +01:00
|
|
|
|
|
|
|
private final PoolArena<byte[]>[] heapArenas;
|
|
|
|
private final PoolArena<ByteBuffer>[] directArenas;
|
2014-03-01 15:47:03 +01:00
|
|
|
private final int tinyCacheSize;
|
|
|
|
private final int smallCacheSize;
|
|
|
|
private final int normalCacheSize;
|
2012-12-05 22:11:48 +01:00
|
|
|
|
2014-04-09 11:07:14 +02:00
|
|
|
final PoolThreadLocalCache threadCache;
|
2012-12-05 22:11:48 +01:00
|
|
|
|
|
|
|
public PooledByteBufAllocator() {
|
|
|
|
this(false);
|
|
|
|
}
|
|
|
|
|
2013-03-05 09:55:24 +01:00
|
|
|
public PooledByteBufAllocator(boolean preferDirect) {
|
|
|
|
this(preferDirect, DEFAULT_NUM_HEAP_ARENA, DEFAULT_NUM_DIRECT_ARENA, DEFAULT_PAGE_SIZE, DEFAULT_MAX_ORDER);
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public PooledByteBufAllocator(int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
|
|
|
|
this(false, nHeapArena, nDirectArena, pageSize, maxOrder);
|
|
|
|
}
|
|
|
|
|
2013-04-27 01:55:16 +02:00
|
|
|
public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
|
2014-03-01 15:47:03 +01:00
|
|
|
this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
|
|
|
|
DEFAULT_TINY_CACHE_SIZE, DEFAULT_SMALL_CACHE_SIZE, DEFAULT_NORMAL_CACHE_SIZE);
|
|
|
|
}
|
2012-12-19 09:35:32 +01:00
|
|
|
|
2014-03-01 15:47:03 +01:00
|
|
|
public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
|
|
|
|
int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
|
2014-04-09 11:07:14 +02:00
|
|
|
this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder, tinyCacheSize, smallCacheSize,
|
|
|
|
normalCacheSize, DEFAULT_CACHE_CLEANUP_INTERVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
|
|
|
|
int tinyCacheSize, int smallCacheSize, int normalCacheSize,
|
|
|
|
long cacheThreadAliveCheckInterval) {
|
2014-03-01 15:47:03 +01:00
|
|
|
super(preferDirect);
|
2014-04-09 11:07:14 +02:00
|
|
|
threadCache = new PoolThreadLocalCache(cacheThreadAliveCheckInterval);
|
2014-03-01 15:47:03 +01:00
|
|
|
this.tinyCacheSize = tinyCacheSize;
|
|
|
|
this.smallCacheSize = smallCacheSize;
|
|
|
|
this.normalCacheSize = normalCacheSize;
|
2012-12-19 09:35:32 +01:00
|
|
|
final int chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder);
|
|
|
|
|
2013-04-27 01:55:16 +02:00
|
|
|
if (nHeapArena < 0) {
|
|
|
|
throw new IllegalArgumentException("nHeapArena: " + nHeapArena + " (expected: >= 0)");
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
2013-04-27 01:55:16 +02:00
|
|
|
if (nDirectArena < 0) {
|
|
|
|
throw new IllegalArgumentException("nDirectArea: " + nDirectArena + " (expected: >= 0)");
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int pageShifts = validateAndCalculatePageShifts(pageSize);
|
|
|
|
|
2013-04-27 01:55:16 +02:00
|
|
|
if (nHeapArena > 0) {
|
|
|
|
heapArenas = newArenaArray(nHeapArena);
|
|
|
|
for (int i = 0; i < heapArenas.length; i ++) {
|
|
|
|
heapArenas[i] = new PoolArena.HeapArena(this, pageSize, maxOrder, pageShifts, chunkSize);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
heapArenas = null;
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
2013-05-30 20:24:11 +02:00
|
|
|
if (nDirectArena > 0) {
|
2013-04-27 01:55:16 +02:00
|
|
|
directArenas = newArenaArray(nDirectArena);
|
|
|
|
for (int i = 0; i < directArenas.length; i ++) {
|
|
|
|
directArenas[i] = new PoolArena.DirectArena(this, pageSize, maxOrder, pageShifts, chunkSize);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
directArenas = null;
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
private static <T> PoolArena<T>[] newArenaArray(int size) {
|
|
|
|
return new PoolArena[size];
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int validateAndCalculatePageShifts(int pageSize) {
|
|
|
|
if (pageSize < MIN_PAGE_SIZE) {
|
2014-03-17 16:32:39 +01:00
|
|
|
throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: " + MIN_PAGE_SIZE + "+)");
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
2014-03-17 16:32:39 +01:00
|
|
|
if ((pageSize & pageSize - 1) != 0) {
|
|
|
|
throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: power of 2)");
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
2014-03-17 16:32:39 +01:00
|
|
|
|
|
|
|
// Logarithm base 2. At this point we know that pageSize is a power of two.
|
|
|
|
return Integer.SIZE - 1 - Integer.numberOfLeadingZeros(pageSize);
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private static int validateAndCalculateChunkSize(int pageSize, int maxOrder) {
|
|
|
|
if (maxOrder > 14) {
|
|
|
|
throw new IllegalArgumentException("maxOrder: " + maxOrder + " (expected: 0-14)");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the resulting chunkSize does not overflow.
|
|
|
|
int chunkSize = pageSize;
|
|
|
|
for (int i = maxOrder; i > 0; i --) {
|
|
|
|
if (chunkSize > MAX_CHUNK_SIZE / 2) {
|
|
|
|
throw new IllegalArgumentException(String.format(
|
|
|
|
"pageSize (%d) << maxOrder (%d) must not exceed %d", pageSize, maxOrder, MAX_CHUNK_SIZE));
|
|
|
|
}
|
|
|
|
chunkSize <<= 1;
|
|
|
|
}
|
|
|
|
return chunkSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
|
|
|
|
PoolThreadCache cache = threadCache.get();
|
2013-04-27 01:55:16 +02:00
|
|
|
PoolArena<byte[]> heapArena = cache.heapArena;
|
2013-12-04 11:03:32 +01:00
|
|
|
|
|
|
|
ByteBuf buf;
|
2013-04-27 01:55:16 +02:00
|
|
|
if (heapArena != null) {
|
2013-12-04 11:03:32 +01:00
|
|
|
buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
|
2013-04-27 01:55:16 +02:00
|
|
|
} else {
|
2013-12-04 11:03:32 +01:00
|
|
|
buf = new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
|
2013-04-27 01:55:16 +02:00
|
|
|
}
|
2013-12-04 11:03:32 +01:00
|
|
|
|
|
|
|
return toLeakAwareBuffer(buf);
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
|
|
|
|
PoolThreadCache cache = threadCache.get();
|
2013-04-27 01:55:16 +02:00
|
|
|
PoolArena<ByteBuffer> directArena = cache.directArena;
|
2013-12-04 11:03:32 +01:00
|
|
|
|
|
|
|
ByteBuf buf;
|
2013-04-27 01:55:16 +02:00
|
|
|
if (directArena != null) {
|
2013-12-04 11:03:32 +01:00
|
|
|
buf = directArena.allocate(cache, initialCapacity, maxCapacity);
|
2013-04-27 01:55:16 +02:00
|
|
|
} else {
|
2013-05-01 04:14:21 +02:00
|
|
|
if (PlatformDependent.hasUnsafe()) {
|
2013-12-04 11:03:32 +01:00
|
|
|
buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
|
2013-05-01 04:14:21 +02:00
|
|
|
} else {
|
2013-12-04 11:03:32 +01:00
|
|
|
buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
|
2013-05-01 04:14:21 +02:00
|
|
|
}
|
2013-04-27 01:55:16 +02:00
|
|
|
}
|
2013-12-04 11:03:32 +01:00
|
|
|
|
|
|
|
return toLeakAwareBuffer(buf);
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|
|
|
|
|
2013-08-16 21:53:47 +02:00
|
|
|
@Override
|
|
|
|
public boolean isDirectBufferPooled() {
|
|
|
|
return directArenas != null;
|
|
|
|
}
|
|
|
|
|
2014-03-01 15:47:03 +01:00
|
|
|
/**
|
|
|
|
* Returns {@code true} if the calling {@link Thread} has a {@link ThreadLocal} cache for the allocated
|
|
|
|
* buffers.
|
|
|
|
*/
|
2014-04-09 11:07:14 +02:00
|
|
|
@Deprecated
|
2014-03-01 15:47:03 +01:00
|
|
|
public boolean hasThreadLocalCache() {
|
|
|
|
return threadCache.exists();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free all cached buffers for the calling {@link Thread}.
|
|
|
|
*/
|
2014-04-09 11:07:14 +02:00
|
|
|
@Deprecated
|
2014-03-01 15:47:03 +01:00
|
|
|
public void freeThreadLocalCache() {
|
|
|
|
threadCache.free();
|
|
|
|
}
|
|
|
|
|
|
|
|
final class PoolThreadLocalCache extends ThreadLocal<PoolThreadCache> {
|
2014-04-09 11:07:14 +02:00
|
|
|
private final Map<Thread, PoolThreadCache> caches = new IdentityHashMap<Thread, PoolThreadCache>();
|
|
|
|
private final ReleaseCacheTask task = new ReleaseCacheTask();
|
2014-03-01 15:47:03 +01:00
|
|
|
private final AtomicInteger index = new AtomicInteger();
|
2014-04-09 11:07:14 +02:00
|
|
|
private final long cacheThreadAliveCheckInterval;
|
|
|
|
|
|
|
|
PoolThreadLocalCache(long cacheThreadAliveCheckInterval) {
|
|
|
|
this.cacheThreadAliveCheckInterval = cacheThreadAliveCheckInterval;
|
|
|
|
}
|
2014-03-01 15:47:03 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public PoolThreadCache get() {
|
|
|
|
PoolThreadCache cache = super.get();
|
|
|
|
if (cache == null) {
|
|
|
|
final int idx = index.getAndIncrement();
|
|
|
|
final PoolArena<byte[]> heapArena;
|
|
|
|
final PoolArena<ByteBuffer> directArena;
|
|
|
|
|
|
|
|
if (heapArenas != null) {
|
|
|
|
heapArena = heapArenas[Math.abs(idx % heapArenas.length)];
|
|
|
|
} else {
|
|
|
|
heapArena = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (directArenas != null) {
|
|
|
|
directArena = directArenas[Math.abs(idx % directArenas.length)];
|
|
|
|
} else {
|
|
|
|
directArena = null;
|
|
|
|
}
|
|
|
|
// If the current Thread is assigned to an EventExecutor we can
|
|
|
|
// easily free the cached stuff again once the EventExecutor completes later.
|
|
|
|
cache = new PoolThreadCache(
|
|
|
|
heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
|
|
|
|
DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
|
|
|
|
set(cache);
|
|
|
|
}
|
|
|
|
return cache;
|
|
|
|
}
|
|
|
|
|
2014-04-09 11:07:14 +02:00
|
|
|
@Override
|
|
|
|
public void set(PoolThreadCache value) {
|
|
|
|
Thread current = Thread.currentThread();
|
|
|
|
synchronized (caches) {
|
|
|
|
caches.put(current, value);
|
|
|
|
if (task.releaseTaskFuture == null) {
|
|
|
|
task.releaseTaskFuture = GlobalEventExecutor.INSTANCE.scheduleWithFixedDelay(task,
|
|
|
|
cacheThreadAliveCheckInterval, cacheThreadAliveCheckInterval, TimeUnit.MILLISECONDS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
super.set(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void remove() {
|
|
|
|
super.remove();
|
|
|
|
PoolThreadCache cache;
|
|
|
|
Thread current = Thread.currentThread();
|
|
|
|
synchronized (caches) {
|
|
|
|
cache = caches.remove(current);
|
|
|
|
}
|
|
|
|
if (cache != null) {
|
|
|
|
cache.free();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-01 15:47:03 +01:00
|
|
|
/**
|
|
|
|
* Returns {@code true} if the calling {@link Thread} has a {@link ThreadLocal} cache for the allocated
|
|
|
|
* buffers.
|
|
|
|
*/
|
2014-04-09 11:07:14 +02:00
|
|
|
@Deprecated
|
2014-03-01 15:47:03 +01:00
|
|
|
public boolean exists() {
|
|
|
|
return super.get() != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free all cached buffers for the calling {@link Thread}.
|
|
|
|
*/
|
2014-04-09 11:07:14 +02:00
|
|
|
@Deprecated
|
2014-03-01 15:47:03 +01:00
|
|
|
public void free() {
|
|
|
|
PoolThreadCache cache = super.get();
|
|
|
|
if (cache != null) {
|
|
|
|
cache.free();
|
|
|
|
}
|
|
|
|
}
|
2014-04-09 11:07:14 +02:00
|
|
|
|
|
|
|
private final class ReleaseCacheTask implements Runnable {
|
|
|
|
private ScheduledFuture<?> releaseTaskFuture;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
synchronized (caches) {
|
|
|
|
for (Iterator<Map.Entry<Thread, PoolThreadCache>> i = caches.entrySet().iterator();
|
|
|
|
i.hasNext();) {
|
|
|
|
Map.Entry<Thread, PoolThreadCache> cache = i.next();
|
|
|
|
if (cache.getKey().isAlive()) {
|
|
|
|
// Thread is still alive...
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
cache.getValue().free();
|
|
|
|
i.remove();
|
|
|
|
}
|
|
|
|
if (caches.isEmpty()) {
|
|
|
|
// Nothing in the caches anymore so no need to continue to check if something needs to be
|
|
|
|
// released periodically. The task will be rescheduled if there is any need later.
|
|
|
|
if (releaseTaskFuture != null) {
|
|
|
|
releaseTaskFuture.cancel(true);
|
|
|
|
releaseTaskFuture = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-03-01 15:47:03 +01:00
|
|
|
}
|
|
|
|
|
2013-11-08 09:53:57 +01:00
|
|
|
// Too noisy at the moment.
|
|
|
|
//
|
|
|
|
// public String toString() {
|
|
|
|
// StringBuilder buf = new StringBuilder();
|
|
|
|
// buf.append(heapArenas.length);
|
|
|
|
// buf.append(" heap arena(s):");
|
|
|
|
// buf.append(StringUtil.NEWLINE);
|
|
|
|
// for (PoolArena<byte[]> a: heapArenas) {
|
|
|
|
// buf.append(a);
|
|
|
|
// }
|
|
|
|
// buf.append(directArenas.length);
|
|
|
|
// buf.append(" direct arena(s):");
|
|
|
|
// buf.append(StringUtil.NEWLINE);
|
|
|
|
// for (PoolArena<ByteBuffer> a: directArenas) {
|
|
|
|
// buf.append(a);
|
|
|
|
// }
|
|
|
|
// return buf.toString();
|
|
|
|
// }
|
2012-12-05 22:11:48 +01:00
|
|
|
}
|