View Javadoc
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 io.netty.util.NettyRuntime;
20  import io.netty.util.concurrent.FastThreadLocal;
21  import io.netty.util.concurrent.FastThreadLocalThread;
22  import io.netty.util.internal.PlatformDependent;
23  import io.netty.util.internal.StringUtil;
24  import io.netty.util.internal.SystemPropertyUtil;
25  import io.netty.util.internal.logging.InternalLogger;
26  import io.netty.util.internal.logging.InternalLoggerFactory;
27  
28  import java.nio.ByteBuffer;
29  import java.util.ArrayList;
30  import java.util.Collections;
31  import java.util.List;
32  
33  public class PooledByteBufAllocator extends AbstractByteBufAllocator implements ByteBufAllocatorMetricProvider {
34  
35      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PooledByteBufAllocator.class);
36      private static final int DEFAULT_NUM_HEAP_ARENA;
37      private static final int DEFAULT_NUM_DIRECT_ARENA;
38  
39      private static final int DEFAULT_PAGE_SIZE;
40      private static final int DEFAULT_MAX_ORDER; // 8192 << 11 = 16 MiB per chunk
41      private static final int DEFAULT_TINY_CACHE_SIZE;
42      private static final int DEFAULT_SMALL_CACHE_SIZE;
43      private static final int DEFAULT_NORMAL_CACHE_SIZE;
44      private static final int DEFAULT_MAX_CACHED_BUFFER_CAPACITY;
45      private static final int DEFAULT_CACHE_TRIM_INTERVAL;
46      private static final boolean DEFAULT_USE_CACHE_FOR_ALL_THREADS;
47      private static final int DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT;
48  
49      private static final int MIN_PAGE_SIZE = 4096;
50      private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2);
51  
52      static {
53          int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192);
54          Throwable pageSizeFallbackCause = null;
55          try {
56              validateAndCalculatePageShifts(defaultPageSize);
57          } catch (Throwable t) {
58              pageSizeFallbackCause = t;
59              defaultPageSize = 8192;
60          }
61          DEFAULT_PAGE_SIZE = defaultPageSize;
62  
63          int defaultMaxOrder = SystemPropertyUtil.getInt("io.netty.allocator.maxOrder", 11);
64          Throwable maxOrderFallbackCause = null;
65          try {
66              validateAndCalculateChunkSize(DEFAULT_PAGE_SIZE, defaultMaxOrder);
67          } catch (Throwable t) {
68              maxOrderFallbackCause = t;
69              defaultMaxOrder = 11;
70          }
71          DEFAULT_MAX_ORDER = defaultMaxOrder;
72  
73          // Determine reasonable default for nHeapArena and nDirectArena.
74          // Assuming each arena has 3 chunks, the pool should not consume more than 50% of max memory.
75          final Runtime runtime = Runtime.getRuntime();
76  
77          /*
78           * We use 2 * available processors by default to reduce contention as we use 2 * available processors for the
79           * number of EventLoops in NIO and EPOLL as well. If we choose a smaller number we will run into hot spots as
80           * allocation and de-allocation needs to be synchronized on the PoolArena.
81           *
82           * See https://github.com/netty/netty/issues/3888.
83           */
84          final int defaultMinNumArena = NettyRuntime.availableProcessors() * 2;
85          final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;
86          DEFAULT_NUM_HEAP_ARENA = Math.max(0,
87                  SystemPropertyUtil.getInt(
88                          "io.netty.allocator.numHeapArenas",
89                          (int) Math.min(
90                                  defaultMinNumArena,
91                                  runtime.maxMemory() / defaultChunkSize / 2 / 3)));
92          DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
93                  SystemPropertyUtil.getInt(
94                          "io.netty.allocator.numDirectArenas",
95                          (int) Math.min(
96                                  defaultMinNumArena,
97                                  PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));
98  
99          // cache sizes
100         DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);
101         DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
102         DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);
103 
104         // 32 kb is the default maximum capacity of the cached buffer. Similar to what is explained in
105         // 'Scalable memory allocation using jemalloc'
106         DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
107                 "io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
108 
109         // the number of threshold of allocations when cached entries will be freed up if not frequently used
110         DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt(
111                 "io.netty.allocator.cacheTrimInterval", 8192);
112 
113         DEFAULT_USE_CACHE_FOR_ALL_THREADS = SystemPropertyUtil.getBoolean(
114                 "io.netty.allocator.useCacheForAllThreads", true);
115 
116         DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = SystemPropertyUtil.getInt(
117                 "io.netty.allocator.directMemoryCacheAlignment", 0);
118 
119         if (logger.isDebugEnabled()) {
120             logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA);
121             logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);
122             if (pageSizeFallbackCause == null) {
123                 logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE);
124             } else {
125                 logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause);
126             }
127             if (maxOrderFallbackCause == null) {
128                 logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER);
129             } else {
130                 logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause);
131             }
132             logger.debug("-Dio.netty.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER);
133             logger.debug("-Dio.netty.allocator.tinyCacheSize: {}", DEFAULT_TINY_CACHE_SIZE);
134             logger.debug("-Dio.netty.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE);
135             logger.debug("-Dio.netty.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE);
136             logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY);
137             logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL);
138             logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS);
139         }
140     }
141 
142     public static final PooledByteBufAllocator DEFAULT =
143             new PooledByteBufAllocator(PlatformDependent.directBufferPreferred());
144 
145     private final PoolArena<byte[]>[] heapArenas;
146     private final PoolArena<ByteBuffer>[] directArenas;
147     private final int tinyCacheSize;
148     private final int smallCacheSize;
149     private final int normalCacheSize;
150     private final List<PoolArenaMetric> heapArenaMetrics;
151     private final List<PoolArenaMetric> directArenaMetrics;
152     private final PoolThreadLocalCache threadCache;
153     private final int chunkSize;
154     private final PooledByteBufAllocatorMetric metric;
155 
156     public PooledByteBufAllocator() {
157         this(false);
158     }
159 
160     @SuppressWarnings("deprecation")
161     public PooledByteBufAllocator(boolean preferDirect) {
162         this(preferDirect, DEFAULT_NUM_HEAP_ARENA, DEFAULT_NUM_DIRECT_ARENA, DEFAULT_PAGE_SIZE, DEFAULT_MAX_ORDER);
163     }
164 
165     @SuppressWarnings("deprecation")
166     public PooledByteBufAllocator(int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
167         this(false, nHeapArena, nDirectArena, pageSize, maxOrder);
168     }
169 
170     /**
171      * @deprecated use
172      * {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int, int, int, int, int, boolean)}
173      */
174     @Deprecated
175     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
176         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
177                 DEFAULT_TINY_CACHE_SIZE, DEFAULT_SMALL_CACHE_SIZE, DEFAULT_NORMAL_CACHE_SIZE);
178     }
179 
180     /**
181      * @deprecated use
182      * {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int, int, int, int, int, boolean)}
183      */
184     @Deprecated
185     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
186                                   int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
187         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder, tinyCacheSize, smallCacheSize,
188                 normalCacheSize, DEFAULT_USE_CACHE_FOR_ALL_THREADS, DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT);
189     }
190 
191     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena,
192                                   int nDirectArena, int pageSize, int maxOrder, int tinyCacheSize,
193                                   int smallCacheSize, int normalCacheSize,
194                                   boolean useCacheForAllThreads) {
195         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
196                 tinyCacheSize, smallCacheSize, normalCacheSize,
197                 useCacheForAllThreads, DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT);
198     }
199 
200     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
201                                   int tinyCacheSize, int smallCacheSize, int normalCacheSize,
202                                   boolean useCacheForAllThreads, int directMemoryCacheAlignment) {
203         super(preferDirect);
204         threadCache = new PoolThreadLocalCache(useCacheForAllThreads);
205         this.tinyCacheSize = tinyCacheSize;
206         this.smallCacheSize = smallCacheSize;
207         this.normalCacheSize = normalCacheSize;
208         chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder);
209 
210         if (nHeapArena < 0) {
211             throw new IllegalArgumentException("nHeapArena: " + nHeapArena + " (expected: >= 0)");
212         }
213         if (nDirectArena < 0) {
214             throw new IllegalArgumentException("nDirectArea: " + nDirectArena + " (expected: >= 0)");
215         }
216 
217         if (directMemoryCacheAlignment < 0) {
218             throw new IllegalArgumentException("directMemoryCacheAlignment: "
219                     + directMemoryCacheAlignment + " (expected: >= 0)");
220         }
221         if (directMemoryCacheAlignment > 0 && !isDirectMemoryCacheAlignmentSupported()) {
222             throw new IllegalArgumentException("directMemoryCacheAlignment is not supported");
223         }
224 
225         if ((directMemoryCacheAlignment & -directMemoryCacheAlignment) != directMemoryCacheAlignment) {
226             throw new IllegalArgumentException("directMemoryCacheAlignment: "
227                     + directMemoryCacheAlignment + " (expected: power of two)");
228         }
229 
230         int pageShifts = validateAndCalculatePageShifts(pageSize);
231 
232         if (nHeapArena > 0) {
233             heapArenas = newArenaArray(nHeapArena);
234             List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(heapArenas.length);
235             for (int i = 0; i < heapArenas.length; i ++) {
236                 PoolArena.HeapArena arena = new PoolArena.HeapArena(this,
237                         pageSize, maxOrder, pageShifts, chunkSize,
238                         directMemoryCacheAlignment);
239                 heapArenas[i] = arena;
240                 metrics.add(arena);
241             }
242             heapArenaMetrics = Collections.unmodifiableList(metrics);
243         } else {
244             heapArenas = null;
245             heapArenaMetrics = Collections.emptyList();
246         }
247 
248         if (nDirectArena > 0) {
249             directArenas = newArenaArray(nDirectArena);
250             List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(directArenas.length);
251             for (int i = 0; i < directArenas.length; i ++) {
252                 PoolArena.DirectArena arena = new PoolArena.DirectArena(
253                         this, pageSize, maxOrder, pageShifts, chunkSize, directMemoryCacheAlignment);
254                 directArenas[i] = arena;
255                 metrics.add(arena);
256             }
257             directArenaMetrics = Collections.unmodifiableList(metrics);
258         } else {
259             directArenas = null;
260             directArenaMetrics = Collections.emptyList();
261         }
262         metric = new PooledByteBufAllocatorMetric(this);
263     }
264 
265     @Deprecated
266     @SuppressWarnings("UnusedParameters")
267     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
268                                   int tinyCacheSize, int smallCacheSize, int normalCacheSize,
269                                   long cacheThreadAliveCheckInterval) {
270         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
271              tinyCacheSize, smallCacheSize, normalCacheSize);
272     }
273 
274     @SuppressWarnings("unchecked")
275     private static <T> PoolArena<T>[] newArenaArray(int size) {
276         return new PoolArena[size];
277     }
278 
279     private static int validateAndCalculatePageShifts(int pageSize) {
280         if (pageSize < MIN_PAGE_SIZE) {
281             throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: " + MIN_PAGE_SIZE + ")");
282         }
283 
284         if ((pageSize & pageSize - 1) != 0) {
285             throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: power of 2)");
286         }
287 
288         // Logarithm base 2. At this point we know that pageSize is a power of two.
289         return Integer.SIZE - 1 - Integer.numberOfLeadingZeros(pageSize);
290     }
291 
292     private static int validateAndCalculateChunkSize(int pageSize, int maxOrder) {
293         if (maxOrder > 14) {
294             throw new IllegalArgumentException("maxOrder: " + maxOrder + " (expected: 0-14)");
295         }
296 
297         // Ensure the resulting chunkSize does not overflow.
298         int chunkSize = pageSize;
299         for (int i = maxOrder; i > 0; i --) {
300             if (chunkSize > MAX_CHUNK_SIZE / 2) {
301                 throw new IllegalArgumentException(String.format(
302                         "pageSize (%d) << maxOrder (%d) must not exceed %d", pageSize, maxOrder, MAX_CHUNK_SIZE));
303             }
304             chunkSize <<= 1;
305         }
306         return chunkSize;
307     }
308 
309     @Override
310     protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
311         PoolThreadCache cache = threadCache.get();
312         PoolArena<byte[]> heapArena = cache.heapArena;
313 
314         final ByteBuf buf;
315         if (heapArena != null) {
316             buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
317         } else {
318             buf = PlatformDependent.hasUnsafe() ?
319                     new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) :
320                     new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
321         }
322 
323         return toLeakAwareBuffer(buf);
324     }
325 
326     @Override
327     protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
328         PoolThreadCache cache = threadCache.get();
329         PoolArena<ByteBuffer> directArena = cache.directArena;
330 
331         final ByteBuf buf;
332         if (directArena != null) {
333             buf = directArena.allocate(cache, initialCapacity, maxCapacity);
334         } else {
335             buf = PlatformDependent.hasUnsafe() ?
336                     UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
337                     new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
338         }
339 
340         return toLeakAwareBuffer(buf);
341     }
342 
343     /**
344      * Default number of heap arenas - System Property: io.netty.allocator.numHeapArenas - default 2 * cores
345      */
346     public static int defaultNumHeapArena() {
347         return DEFAULT_NUM_HEAP_ARENA;
348     }
349 
350     /**
351      * Default number of direct arenas - System Property: io.netty.allocator.numDirectArenas - default 2 * cores
352      */
353     public static int defaultNumDirectArena() {
354         return DEFAULT_NUM_DIRECT_ARENA;
355     }
356 
357     /**
358      * Default buffer page size - System Property: io.netty.allocator.pageSize - default 8192
359      */
360     public static int defaultPageSize() {
361         return DEFAULT_PAGE_SIZE;
362     }
363 
364     /**
365      * Default maximum order - System Property: io.netty.allocator.maxOrder - default 11
366      */
367     public static int defaultMaxOrder() {
368         return DEFAULT_MAX_ORDER;
369     }
370 
371     /**
372      * Default thread caching behavior - System Property: io.netty.allocator.useCacheForAllThreads - default true
373      */
374     public static boolean defaultUseCacheForAllThreads() {
375         return DEFAULT_USE_CACHE_FOR_ALL_THREADS;
376     }
377 
378     /**
379      * Default prefer direct - System Property: io.netty.noPreferDirect - default false
380      */
381     public static boolean defaultPreferDirect() {
382         return PlatformDependent.directBufferPreferred();
383     }
384 
385     /**
386      * Default tiny cache size - System Property: io.netty.allocator.tinyCacheSize - default 512
387      */
388     public static int defaultTinyCacheSize() {
389         return DEFAULT_TINY_CACHE_SIZE;
390     }
391 
392     /**
393      * Default small cache size - System Property: io.netty.allocator.smallCacheSize - default 256
394      */
395     public static int defaultSmallCacheSize() {
396         return DEFAULT_SMALL_CACHE_SIZE;
397     }
398 
399     /**
400      * Default normal cache size - System Property: io.netty.allocator.normalCacheSize - default 64
401      */
402     public static int defaultNormalCacheSize() {
403         return DEFAULT_NORMAL_CACHE_SIZE;
404     }
405 
406     /**
407      * Return {@code true} if direct memory cache alignment is supported, {@code false} otherwise.
408      */
409     public static boolean isDirectMemoryCacheAlignmentSupported() {
410         return PlatformDependent.hasUnsafe();
411     }
412 
413     @Override
414     public boolean isDirectBufferPooled() {
415         return directArenas != null;
416     }
417 
418     /**
419      * Returns {@code true} if the calling {@link Thread} has a {@link ThreadLocal} cache for the allocated
420      * buffers.
421      */
422     @Deprecated
423     public boolean hasThreadLocalCache() {
424         return threadCache.isSet();
425     }
426 
427     /**
428      * Free all cached buffers for the calling {@link Thread}.
429      */
430     @Deprecated
431     public void freeThreadLocalCache() {
432         threadCache.remove();
433     }
434 
435     final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
436         private final boolean useCacheForAllThreads;
437 
438         PoolThreadLocalCache(boolean useCacheForAllThreads) {
439             this.useCacheForAllThreads = useCacheForAllThreads;
440         }
441 
442         @Override
443         protected synchronized PoolThreadCache initialValue() {
444             final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
445             final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
446 
447             Thread current = Thread.currentThread();
448             if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {
449                 return new PoolThreadCache(
450                         heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
451                         DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
452             }
453             // No caching so just use 0 as sizes.
454             return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0, 0);
455         }
456 
457         @Override
458         protected void onRemoval(PoolThreadCache threadCache) {
459             threadCache.free();
460         }
461 
462         private <T> PoolArena<T> leastUsedArena(PoolArena<T>[] arenas) {
463             if (arenas == null || arenas.length == 0) {
464                 return null;
465             }
466 
467             PoolArena<T> minArena = arenas[0];
468             for (int i = 1; i < arenas.length; i++) {
469                 PoolArena<T> arena = arenas[i];
470                 if (arena.numThreadCaches.get() < minArena.numThreadCaches.get()) {
471                     minArena = arena;
472                 }
473             }
474 
475             return minArena;
476         }
477     }
478 
479     @Override
480     public PooledByteBufAllocatorMetric metric() {
481         return metric;
482     }
483 
484     /**
485      * Return the number of heap arenas.
486      *
487      * @deprecated use {@link PooledByteBufAllocatorMetric#numHeapArenas()}.
488      */
489     @Deprecated
490     public int numHeapArenas() {
491         return heapArenaMetrics.size();
492     }
493 
494     /**
495      * Return the number of direct arenas.
496      *
497      * @deprecated use {@link PooledByteBufAllocatorMetric#numDirectArenas()}.
498      */
499     @Deprecated
500     public int numDirectArenas() {
501         return directArenaMetrics.size();
502     }
503 
504     /**
505      * Return a {@link List} of all heap {@link PoolArenaMetric}s that are provided by this pool.
506      *
507      * @deprecated use {@link PooledByteBufAllocatorMetric#heapArenas()}.
508      */
509     @Deprecated
510     public List<PoolArenaMetric> heapArenas() {
511         return heapArenaMetrics;
512     }
513 
514     /**
515      * Return a {@link List} of all direct {@link PoolArenaMetric}s that are provided by this pool.
516      *
517      * @deprecated use {@link PooledByteBufAllocatorMetric#directArenas()}.
518      */
519     @Deprecated
520     public List<PoolArenaMetric> directArenas() {
521         return directArenaMetrics;
522     }
523 
524     /**
525      * Return the number of thread local caches used by this {@link PooledByteBufAllocator}.
526      *
527      * @deprecated use {@link PooledByteBufAllocatorMetric#numThreadLocalCaches()}.
528      */
529     @Deprecated
530     public int numThreadLocalCaches() {
531         PoolArena<?>[] arenas = heapArenas != null ? heapArenas : directArenas;
532         if (arenas == null) {
533             return 0;
534         }
535 
536         int total = 0;
537         for (PoolArena<?> arena : arenas) {
538             total += arena.numThreadCaches.get();
539         }
540 
541         return total;
542     }
543 
544     /**
545      * Return the size of the tiny cache.
546      *
547      * @deprecated use {@link PooledByteBufAllocatorMetric#tinyCacheSize()}.
548      */
549     @Deprecated
550     public int tinyCacheSize() {
551         return tinyCacheSize;
552     }
553 
554     /**
555      * Return the size of the small cache.
556      *
557      * @deprecated use {@link PooledByteBufAllocatorMetric#smallCacheSize()}.
558      */
559     @Deprecated
560     public int smallCacheSize() {
561         return smallCacheSize;
562     }
563 
564     /**
565      * Return the size of the normal cache.
566      *
567      * @deprecated use {@link PooledByteBufAllocatorMetric#normalCacheSize()}.
568      */
569     @Deprecated
570     public int normalCacheSize() {
571         return normalCacheSize;
572     }
573 
574     /**
575      * Return the chunk size for an arena.
576      *
577      * @deprecated use {@link PooledByteBufAllocatorMetric#chunkSize()}.
578      */
579     @Deprecated
580     public final int chunkSize() {
581         return chunkSize;
582     }
583 
584     final long usedHeapMemory() {
585         return usedMemory(heapArenas);
586     }
587 
588     final long usedDirectMemory() {
589         return usedMemory(directArenas);
590     }
591 
592     private static long usedMemory(PoolArena<?>... arenas) {
593         if (arenas == null) {
594             return -1;
595         }
596         long used = 0;
597         for (PoolArena<?> arena : arenas) {
598             used += arena.numActiveBytes();
599             if (used < 0) {
600                 return Long.MAX_VALUE;
601             }
602         }
603         return used;
604     }
605 
606     final PoolThreadCache threadCache() {
607         PoolThreadCache cache =  threadCache.get();
608         assert cache != null;
609         return cache;
610     }
611 
612     /**
613      * Returns the status of the allocator (which contains all metrics) as string. Be aware this may be expensive
614      * and so should not called too frequently.
615      */
616     public String dumpStats() {
617         int heapArenasLen = heapArenas == null ? 0 : heapArenas.length;
618         StringBuilder buf = new StringBuilder(512)
619                 .append(heapArenasLen)
620                 .append(" heap arena(s):")
621                 .append(StringUtil.NEWLINE);
622         if (heapArenasLen > 0) {
623             for (PoolArena<byte[]> a: heapArenas) {
624                 buf.append(a);
625             }
626         }
627 
628         int directArenasLen = directArenas == null ? 0 : directArenas.length;
629 
630         buf.append(directArenasLen)
631            .append(" direct arena(s):")
632            .append(StringUtil.NEWLINE);
633         if (directArenasLen > 0) {
634             for (PoolArena<ByteBuffer> a: directArenas) {
635                 buf.append(a);
636             }
637         }
638 
639         return buf.toString();
640     }
641 }