1 /*
2  * Copyright 2012 The Netty Project
3  *
4  * The Netty Project licenses this file to you under the Apache License,
5  * version 2.0 (the "License"); you may not use this file except in compliance
6  * with the License. You may obtain a copy of the License at:
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */

16
17 package io.netty.buffer;
18
19 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
20
21 import io.netty.util.NettyRuntime;
22 import io.netty.util.concurrent.EventExecutor;
23 import io.netty.util.concurrent.FastThreadLocal;
24 import io.netty.util.concurrent.FastThreadLocalThread;
25 import io.netty.util.internal.PlatformDependent;
26 import io.netty.util.internal.StringUtil;
27 import io.netty.util.internal.SystemPropertyUtil;
28 import io.netty.util.internal.ThreadExecutorMap;
29 import io.netty.util.internal.logging.InternalLogger;
30 import io.netty.util.internal.logging.InternalLoggerFactory;
31
32 import java.nio.ByteBuffer;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.concurrent.TimeUnit;
37
38 public class PooledByteBufAllocator extends AbstractByteBufAllocator implements ByteBufAllocatorMetricProvider {
39
40     private static final InternalLogger logger = InternalLoggerFactory.getInstance(PooledByteBufAllocator.class);
41     private static final int DEFAULT_NUM_HEAP_ARENA;
42     private static final int DEFAULT_NUM_DIRECT_ARENA;
43
44     private static final int DEFAULT_PAGE_SIZE;
45     private static final int DEFAULT_MAX_ORDER; // 8192 << 11 = 16 MiB per chunk
46     private static final int DEFAULT_SMALL_CACHE_SIZE;
47     private static final int DEFAULT_NORMAL_CACHE_SIZE;
48     private static final int DEFAULT_MAX_CACHED_BUFFER_CAPACITY;
49     private static final int DEFAULT_CACHE_TRIM_INTERVAL;
50     private static final long DEFAULT_CACHE_TRIM_INTERVAL_MILLIS;
51     private static final boolean DEFAULT_USE_CACHE_FOR_ALL_THREADS;
52     private static final int DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT;
53     static final int DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK;
54
55     private static final int MIN_PAGE_SIZE = 4096;
56     private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2);
57
58     private final Runnable trimTask = new Runnable() {
59         @Override
60         public void run() {
61             PooledByteBufAllocator.this.trimCurrentThreadCache();
62         }
63     };
64
65     static {
66         int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192);
67         Throwable pageSizeFallbackCause = null;
68         try {
69             validateAndCalculatePageShifts(defaultPageSize);
70         } catch (Throwable t) {
71             pageSizeFallbackCause = t;
72             defaultPageSize = 8192;
73         }
74         DEFAULT_PAGE_SIZE = defaultPageSize;
75
76         int defaultMaxOrder = SystemPropertyUtil.getInt("io.netty.allocator.maxOrder", 11);
77         Throwable maxOrderFallbackCause = null;
78         try {
79             validateAndCalculateChunkSize(DEFAULT_PAGE_SIZE, defaultMaxOrder);
80         } catch (Throwable t) {
81             maxOrderFallbackCause = t;
82             defaultMaxOrder = 11;
83         }
84         DEFAULT_MAX_ORDER = defaultMaxOrder;
85
86         // Determine reasonable default for nHeapArena and nDirectArena.
87         // Assuming each arena has 3 chunks, the pool should not consume more than 50% of max memory.
88         final Runtime runtime = Runtime.getRuntime();
89
90         /*
91          * We use 2 * available processors by default to reduce contention as we use 2 * available processors for the
92          * number of EventLoops in NIO and EPOLL as well. If we choose a smaller number we will run into hot spots as
93          * allocation and de-allocation needs to be synchronized on the PoolArena.
94          *
95          * See https://github.com/netty/netty/issues/3888.
96          */

97         final int defaultMinNumArena = NettyRuntime.availableProcessors() * 2;
98         final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;
99         DEFAULT_NUM_HEAP_ARENA = Math.max(0,
100                 SystemPropertyUtil.getInt(
101                         "io.netty.allocator.numHeapArenas",
102                         (int) Math.min(
103                                 defaultMinNumArena,
104                                 runtime.maxMemory() / defaultChunkSize / 2 / 3)));
105         DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
106                 SystemPropertyUtil.getInt(
107                         "io.netty.allocator.numDirectArenas",
108                         (int) Math.min(
109                                 defaultMinNumArena,
110                                 PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));
111
112         // cache sizes
113         DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
114         DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);
115
116         // 32 kb is the default maximum capacity of the cached buffer. Similar to what is explained in
117         // 'Scalable memory allocation using jemalloc'
118         DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
119                 "io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
120
121         // the number of threshold of allocations when cached entries will be freed up if not frequently used
122         DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt(
123                 "io.netty.allocator.cacheTrimInterval", 8192);
124
125         if (SystemPropertyUtil.contains("io.netty.allocation.cacheTrimIntervalMillis")) {
126             logger.warn("-Dio.netty.allocation.cacheTrimIntervalMillis is deprecated," +
127                     " use -Dio.netty.allocator.cacheTrimIntervalMillis");
128
129             if (SystemPropertyUtil.contains("io.netty.allocator.cacheTrimIntervalMillis")) {
130                 // Both system properties are specified. Use the non-deprecated one.
131                 DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong(
132                         "io.netty.allocator.cacheTrimIntervalMillis", 0);
133             } else {
134                 DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong(
135                         "io.netty.allocation.cacheTrimIntervalMillis", 0);
136             }
137         } else {
138             DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong(
139                     "io.netty.allocator.cacheTrimIntervalMillis", 0);
140         }
141
142         DEFAULT_USE_CACHE_FOR_ALL_THREADS = SystemPropertyUtil.getBoolean(
143                 "io.netty.allocator.useCacheForAllThreads"true);
144
145         DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = SystemPropertyUtil.getInt(
146                 "io.netty.allocator.directMemoryCacheAlignment", 0);
147
148         // Use 1023 by default as we use an ArrayDeque as backing storage which will then allocate an internal array
149         // of 1024 elements. Otherwise we would allocate 2048 and only use 1024 which is wasteful.
150         DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK = SystemPropertyUtil.getInt(
151                 "io.netty.allocator.maxCachedByteBuffersPerChunk", 1023);
152
153         if (logger.isDebugEnabled()) {
154             logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA);
155             logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);
156             if (pageSizeFallbackCause == null) {
157                 logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE);
158             } else {
159                 logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause);
160             }
161             if (maxOrderFallbackCause == null) {
162                 logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER);
163             } else {
164                 logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause);
165             }
166             logger.debug("-Dio.netty.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER);
167             logger.debug("-Dio.netty.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE);
168             logger.debug("-Dio.netty.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE);
169             logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY);
170             logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL);
171             logger.debug("-Dio.netty.allocator.cacheTrimIntervalMillis: {}", DEFAULT_CACHE_TRIM_INTERVAL_MILLIS);
172             logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS);
173             logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}",
174                     DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK);
175         }
176     }
177
178     public static final PooledByteBufAllocator DEFAULT =
179             new PooledByteBufAllocator(PlatformDependent.directBufferPreferred());
180
181     private final PoolArena<byte[]>[] heapArenas;
182     private final PoolArena<ByteBuffer>[] directArenas;
183     private final int smallCacheSize;
184     private final int normalCacheSize;
185     private final List<PoolArenaMetric> heapArenaMetrics;
186     private final List<PoolArenaMetric> directArenaMetrics;
187     private final PoolThreadLocalCache threadCache;
188     private final int chunkSize;
189     private final PooledByteBufAllocatorMetric metric;
190
191     public PooledByteBufAllocator() {
192         this(false);
193     }
194
195     @SuppressWarnings("deprecation")
196     public PooledByteBufAllocator(boolean preferDirect) {
197         this(preferDirect, DEFAULT_NUM_HEAP_ARENA, DEFAULT_NUM_DIRECT_ARENA, DEFAULT_PAGE_SIZE, DEFAULT_MAX_ORDER);
198     }
199
200     @SuppressWarnings("deprecation")
201     public PooledByteBufAllocator(int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
202         this(false, nHeapArena, nDirectArena, pageSize, maxOrder);
203     }
204
205     /**
206      * @deprecated use
207      * {@link PooledByteBufAllocator#PooledByteBufAllocator(booleanintintintintintintboolean)}
208      */

209     @Deprecated
210     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
211         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
212              0, DEFAULT_SMALL_CACHE_SIZE, DEFAULT_NORMAL_CACHE_SIZE);
213     }
214
215     /**
216      * @deprecated use
217      * {@link PooledByteBufAllocator#PooledByteBufAllocator(booleanintintintintintintboolean)}
218      */

219     @Deprecated
220     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
221                                   int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
222         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder, smallCacheSize,
223              normalCacheSize, DEFAULT_USE_CACHE_FOR_ALL_THREADS, DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT);
224     }
225
226     /**
227      * @deprecated use
228      * {@link PooledByteBufAllocator#PooledByteBufAllocator(booleanintintintintintintboolean)}
229      */

230     @Deprecated
231     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena,
232                                   int nDirectArena, int pageSize, int maxOrder, int tinyCacheSize,
233                                   int smallCacheSize, int normalCacheSize,
234                                   boolean useCacheForAllThreads) {
235         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
236              smallCacheSize, normalCacheSize,
237              useCacheForAllThreads);
238     }
239
240     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena,
241                                   int nDirectArena, int pageSize, int maxOrder,
242                                   int smallCacheSize, int normalCacheSize,
243                                   boolean useCacheForAllThreads) {
244         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
245              smallCacheSize, normalCacheSize,
246              useCacheForAllThreads, DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT);
247     }
248
249     /**
250      * @deprecated use
251      * {@link PooledByteBufAllocator#PooledByteBufAllocator(booleanintintintintintintbooleanint)}
252      */

253     @Deprecated
254     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
255                                   int tinyCacheSize, int smallCacheSize, int normalCacheSize,
256                                   boolean useCacheForAllThreads, int directMemoryCacheAlignment) {
257         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
258              smallCacheSize, normalCacheSize,
259              useCacheForAllThreads, directMemoryCacheAlignment);
260     }
261
262     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
263                                   int smallCacheSize, int normalCacheSize,
264                                   boolean useCacheForAllThreads, int directMemoryCacheAlignment) {
265         super(preferDirect);
266         threadCache = new PoolThreadLocalCache(useCacheForAllThreads);
267         this.smallCacheSize = smallCacheSize;
268         this.normalCacheSize = normalCacheSize;
269         chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder);
270
271         checkPositiveOrZero(nHeapArena, "nHeapArena");
272         checkPositiveOrZero(nDirectArena, "nDirectArena");
273
274         checkPositiveOrZero(directMemoryCacheAlignment, "directMemoryCacheAlignment");
275         if (directMemoryCacheAlignment > 0 && !isDirectMemoryCacheAlignmentSupported()) {
276             throw new IllegalArgumentException("directMemoryCacheAlignment is not supported");
277         }
278
279         if ((directMemoryCacheAlignment & -directMemoryCacheAlignment) != directMemoryCacheAlignment) {
280             throw new IllegalArgumentException("directMemoryCacheAlignment: "
281                     + directMemoryCacheAlignment + " (expected: power of two)");
282         }
283
284         int pageShifts = validateAndCalculatePageShifts(pageSize);
285
286         if (nHeapArena > 0) {
287             heapArenas = newArenaArray(nHeapArena);
288             List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(heapArenas.length);
289             for (int i = 0; i < heapArenas.length; i ++) {
290                 PoolArena.HeapArena arena = new PoolArena.HeapArena(this,
291                         pageSize, pageShifts, chunkSize,
292                         directMemoryCacheAlignment);
293                 heapArenas[i] = arena;
294                 metrics.add(arena);
295             }
296             heapArenaMetrics = Collections.unmodifiableList(metrics);
297         } else {
298             heapArenas = null;
299             heapArenaMetrics = Collections.emptyList();
300         }
301
302         if (nDirectArena > 0) {
303             directArenas = newArenaArray(nDirectArena);
304             List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(directArenas.length);
305             for (int i = 0; i < directArenas.length; i ++) {
306                 PoolArena.DirectArena arena = new PoolArena.DirectArena(
307                         this, pageSize, pageShifts, chunkSize, directMemoryCacheAlignment);
308                 directArenas[i] = arena;
309                 metrics.add(arena);
310             }
311             directArenaMetrics = Collections.unmodifiableList(metrics);
312         } else {
313             directArenas = null;
314             directArenaMetrics = Collections.emptyList();
315         }
316         metric = new PooledByteBufAllocatorMetric(this);
317     }
318
319     @SuppressWarnings("unchecked")
320     private static <T> PoolArena<T>[] newArenaArray(int size) {
321         return new PoolArena[size];
322     }
323
324     private static int validateAndCalculatePageShifts(int pageSize) {
325         if (pageSize < MIN_PAGE_SIZE) {
326             throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: " + MIN_PAGE_SIZE + ")");
327         }
328
329         if ((pageSize & pageSize - 1) != 0) {
330             throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: power of 2)");
331         }
332
333         // Logarithm base 2. At this point we know that pageSize is a power of two.
334         return Integer.SIZE - 1 - Integer.numberOfLeadingZeros(pageSize);
335     }
336
337     private static int validateAndCalculateChunkSize(int pageSize, int maxOrder) {
338         if (maxOrder > 14) {
339             throw new IllegalArgumentException("maxOrder: " + maxOrder + " (expected: 0-14)");
340         }
341
342         // Ensure the resulting chunkSize does not overflow.
343         int chunkSize = pageSize;
344         for (int i = maxOrder; i > 0; i --) {
345             if (chunkSize > MAX_CHUNK_SIZE / 2) {
346                 throw new IllegalArgumentException(String.format(
347                         "pageSize (%d) << maxOrder (%d) must not exceed %d", pageSize, maxOrder, MAX_CHUNK_SIZE));
348             }
349             chunkSize <<= 1;
350         }
351         return chunkSize;
352     }
353
354     @Override
355     protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
356         PoolThreadCache cache = threadCache.get();
357         PoolArena<byte[]> heapArena = cache.heapArena;
358
359         final ByteBuf buf;
360         if (heapArena != null) {
361             buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
362         } else {
363             buf = PlatformDependent.hasUnsafe() ?
364                     new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) :
365                     new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
366         }
367
368         return toLeakAwareBuffer(buf);
369     }
370
371     @Override
372     protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
373         PoolThreadCache cache = threadCache.get();
374         PoolArena<ByteBuffer> directArena = cache.directArena;
375
376         final ByteBuf buf;
377         if (directArena != null) {
378             buf = directArena.allocate(cache, initialCapacity, maxCapacity);
379         } else {
380             buf = PlatformDependent.hasUnsafe() ?
381                     UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
382                     new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
383         }
384
385         return toLeakAwareBuffer(buf);
386     }
387
388     /**
389      * Default number of heap arenas - System Property: io.netty.allocator.numHeapArenas - default 2 * cores
390      */

391     public static int defaultNumHeapArena() {
392         return DEFAULT_NUM_HEAP_ARENA;
393     }
394
395     /**
396      * Default number of direct arenas - System Property: io.netty.allocator.numDirectArenas - default 2 * cores
397      */

398     public static int defaultNumDirectArena() {
399         return DEFAULT_NUM_DIRECT_ARENA;
400     }
401
402     /**
403      * Default buffer page size - System Property: io.netty.allocator.pageSize - default 8192
404      */

405     public static int defaultPageSize() {
406         return DEFAULT_PAGE_SIZE;
407     }
408
409     /**
410      * Default maximum order - System Property: io.netty.allocator.maxOrder - default 11
411      */

412     public static int defaultMaxOrder() {
413         return DEFAULT_MAX_ORDER;
414     }
415
416     /**
417      * Default thread caching behavior - System Property: io.netty.allocator.useCacheForAllThreads - default true
418      */

419     public static boolean defaultUseCacheForAllThreads() {
420         return DEFAULT_USE_CACHE_FOR_ALL_THREADS;
421     }
422
423     /**
424      * Default prefer direct - System Property: io.netty.noPreferDirect - default false
425      */

426     public static boolean defaultPreferDirect() {
427         return PlatformDependent.directBufferPreferred();
428     }
429
430     /**
431      * Default tiny cache size - default 0
432      *
433      * @deprecated Tiny caches have been merged into small caches.
434      */

435     @Deprecated
436     public static int defaultTinyCacheSize() {
437         return 0;
438     }
439
440     /**
441      * Default small cache size - System Property: io.netty.allocator.smallCacheSize - default 256
442      */

443     public static int defaultSmallCacheSize() {
444         return DEFAULT_SMALL_CACHE_SIZE;
445     }
446
447     /**
448      * Default normal cache size - System Property: io.netty.allocator.normalCacheSize - default 64
449      */

450     public static int defaultNormalCacheSize() {
451         return DEFAULT_NORMAL_CACHE_SIZE;
452     }
453
454     /**
455      * Return {@code trueif direct memory cache alignment is supported, {@code false} otherwise.
456      */

457     public static boolean isDirectMemoryCacheAlignmentSupported() {
458         return PlatformDependent.hasUnsafe();
459     }
460
461     @Override
462     public boolean isDirectBufferPooled() {
463         return directArenas != null;
464     }
465
466     /**
467      * Returns {@code trueif the calling {@link Thread} has a {@link ThreadLocal} cache for the allocated
468      * buffers.
469      */

470     @Deprecated
471     public boolean hasThreadLocalCache() {
472         return threadCache.isSet();
473     }
474
475     /**
476      * Free all cached buffers for the calling {@link Thread}.
477      */

478     @Deprecated
479     public void freeThreadLocalCache() {
480         threadCache.remove();
481     }
482
483     final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
484         private final boolean useCacheForAllThreads;
485
486         PoolThreadLocalCache(boolean useCacheForAllThreads) {
487             this.useCacheForAllThreads = useCacheForAllThreads;
488         }
489
490         @Override
491         protected synchronized PoolThreadCache initialValue() {
492             final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
493             final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
494
495             final Thread current = Thread.currentThread();
496             if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {
497                 final PoolThreadCache cache = new PoolThreadCache(
498                         heapArena, directArena, smallCacheSize, normalCacheSize,
499                         DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
500
501                 if (DEFAULT_CACHE_TRIM_INTERVAL_MILLIS > 0) {
502                     final EventExecutor executor = ThreadExecutorMap.currentExecutor();
503                     if (executor != null) {
504                         executor.scheduleAtFixedRate(trimTask, DEFAULT_CACHE_TRIM_INTERVAL_MILLIS,
505                                 DEFAULT_CACHE_TRIM_INTERVAL_MILLIS, TimeUnit.MILLISECONDS);
506                     }
507                 }
508                 return cache;
509             }
510             // No caching so just use 0 as sizes.
511             return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0);
512         }
513
514         @Override
515         protected void onRemoval(PoolThreadCache threadCache) {
516             threadCache.free(false);
517         }
518
519         private <T> PoolArena<T> leastUsedArena(PoolArena<T>[] arenas) {
520             if (arenas == null || arenas.length == 0) {
521                 return null;
522             }
523
524             PoolArena<T> minArena = arenas[0];
525             for (int i = 1; i < arenas.length; i++) {
526                 PoolArena<T> arena = arenas[i];
527                 if (arena.numThreadCaches.get() < minArena.numThreadCaches.get()) {
528                     minArena = arena;
529                 }
530             }
531
532             return minArena;
533         }
534     }
535
536     @Override
537     public PooledByteBufAllocatorMetric metric() {
538         return metric;
539     }
540
541     /**
542      * Return the number of heap arenas.
543      *
544      * @deprecated use {@link PooledByteBufAllocatorMetric#numHeapArenas()}.
545      */

546     @Deprecated
547     public int numHeapArenas() {
548         return heapArenaMetrics.size();
549     }
550
551     /**
552      * Return the number of direct arenas.
553      *
554      * @deprecated use {@link PooledByteBufAllocatorMetric#numDirectArenas()}.
555      */

556     @Deprecated
557     public int numDirectArenas() {
558         return directArenaMetrics.size();
559     }
560
561     /**
562      * Return a {@link List} of all heap {@link PoolArenaMetric}s that are provided by this pool.
563      *
564      * @deprecated use {@link PooledByteBufAllocatorMetric#heapArenas()}.
565      */

566     @Deprecated
567     public List<PoolArenaMetric> heapArenas() {
568         return heapArenaMetrics;
569     }
570
571     /**
572      * Return a {@link List} of all direct {@link PoolArenaMetric}s that are provided by this pool.
573      *
574      * @deprecated use {@link PooledByteBufAllocatorMetric#directArenas()}.
575      */

576     @Deprecated
577     public List<PoolArenaMetric> directArenas() {
578         return directArenaMetrics;
579     }
580
581     /**
582      * Return the number of thread local caches used by this {@link PooledByteBufAllocator}.
583      *
584      * @deprecated use {@link PooledByteBufAllocatorMetric#numThreadLocalCaches()}.
585      */

586     @Deprecated
587     public int numThreadLocalCaches() {
588         PoolArena<?>[] arenas = heapArenas != null ? heapArenas : directArenas;
589         if (arenas == null) {
590             return 0;
591         }
592
593         int total = 0;
594         for (PoolArena<?> arena : arenas) {
595             total += arena.numThreadCaches.get();
596         }
597
598         return total;
599     }
600
601     /**
602      * Return the size of the tiny cache.
603      *
604      * @deprecated use {@link PooledByteBufAllocatorMetric#tinyCacheSize()}.
605      */

606     @Deprecated
607     public int tinyCacheSize() {
608         return 0;
609     }
610
611     /**
612      * Return the size of the small cache.
613      *
614      * @deprecated use {@link PooledByteBufAllocatorMetric#smallCacheSize()}.
615      */

616     @Deprecated
617     public int smallCacheSize() {
618         return smallCacheSize;
619     }
620
621     /**
622      * Return the size of the normal cache.
623      *
624      * @deprecated use {@link PooledByteBufAllocatorMetric#normalCacheSize()}.
625      */

626     @Deprecated
627     public int normalCacheSize() {
628         return normalCacheSize;
629     }
630
631     /**
632      * Return the chunk size for an arena.
633      *
634      * @deprecated use {@link PooledByteBufAllocatorMetric#chunkSize()}.
635      */

636     @Deprecated
637     public final int chunkSize() {
638         return chunkSize;
639     }
640
641     final long usedHeapMemory() {
642         return usedMemory(heapArenas);
643     }
644
645     final long usedDirectMemory() {
646         return usedMemory(directArenas);
647     }
648
649     private static long usedMemory(PoolArena<?>[] arenas) {
650         if (arenas == null) {
651             return -1;
652         }
653         long used = 0;
654         for (PoolArena<?> arena : arenas) {
655             used += arena.numActiveBytes();
656             if (used < 0) {
657                 return Long.MAX_VALUE;
658             }
659         }
660         return used;
661     }
662
663     final PoolThreadCache threadCache() {
664         PoolThreadCache cache =  threadCache.get();
665         assert cache != null;
666         return cache;
667     }
668
669     /**
670      * Trim thread local cache for the current {@link Thread}, which will give back any cached memory that was not
671      * allocated frequently since the last trim operation.
672      *
673      * Returns {@code trueif a cache for the current {@link Thread} exists and so was trimmed, false otherwise.
674      */

675     public boolean trimCurrentThreadCache() {
676         PoolThreadCache cache = threadCache.getIfExists();
677         if (cache != null) {
678             cache.trim();
679             return true;
680         }
681         return false;
682     }
683
684     /**
685      * Returns the status of the allocator (which contains all metrics) as string. Be aware this may be expensive
686      * and so should not called too frequently.
687      */

688     public String dumpStats() {
689         int heapArenasLen = heapArenas == null ? 0 : heapArenas.length;
690         StringBuilder buf = new StringBuilder(512)
691                 .append(heapArenasLen)
692                 .append(" heap arena(s):")
693                 .append(StringUtil.NEWLINE);
694         if (heapArenasLen > 0) {
695             for (PoolArena<byte[]> a: heapArenas) {
696                 buf.append(a);
697             }
698         }
699
700         int directArenasLen = directArenas == null ? 0 : directArenas.length;
701
702         buf.append(directArenasLen)
703            .append(" direct arena(s):")
704            .append(StringUtil.NEWLINE);
705         if (directArenasLen > 0) {
706             for (PoolArena<ByteBuffer> a: directArenas) {
707                 buf.append(a);
708             }
709         }
710
711         return buf.toString();
712     }
713 }
714