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    *   https://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 << 9 = 4 MiB per chunk
46      private static final int DEFAULT_SMALL_CACHE_SIZE;
47      private static final int DEFAULT_NORMAL_CACHE_SIZE;
48      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      private static final boolean DEFAULT_DISABLE_CACHE_FINALIZERS_FOR_FAST_THREAD_LOCAL_THREADS;
55  
56      private static final int MIN_PAGE_SIZE = 4096;
57      private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2);
58  
59      private static final int CACHE_NOT_USED = 0;
60  
61      private final Runnable trimTask = new Runnable() {
62          @Override
63          public void run() {
64              PooledByteBufAllocator.this.trimCurrentThreadCache();
65          }
66      };
67  
68      static {
69          int defaultAlignment = SystemPropertyUtil.getInt(
70                  "io.netty.allocator.directMemoryCacheAlignment", 0);
71          int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192);
72          Throwable pageSizeFallbackCause = null;
73          try {
74              validateAndCalculatePageShifts(defaultPageSize, defaultAlignment);
75          } catch (Throwable t) {
76              pageSizeFallbackCause = t;
77              defaultPageSize = 8192;
78              defaultAlignment = 0;
79          }
80          DEFAULT_PAGE_SIZE = defaultPageSize;
81          DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = defaultAlignment;
82  
83          int defaultMaxOrder = SystemPropertyUtil.getInt("io.netty.allocator.maxOrder", 9);
84          Throwable maxOrderFallbackCause = null;
85          try {
86              validateAndCalculateChunkSize(DEFAULT_PAGE_SIZE, defaultMaxOrder);
87          } catch (Throwable t) {
88              maxOrderFallbackCause = t;
89              defaultMaxOrder = 9;
90          }
91          DEFAULT_MAX_ORDER = defaultMaxOrder;
92  
93          // Determine reasonable default for nHeapArena and nDirectArena.
94          // Assuming each arena has 3 chunks, the pool should not consume more than 50% of max memory.
95          final Runtime runtime = Runtime.getRuntime();
96  
97          /*
98           * We use 2 * available processors by default to reduce contention as we use 2 * available processors for the
99           * number of EventLoops in NIO and EPOLL as well. If we choose a smaller number we will run into hot spots as
100          * allocation and de-allocation needs to be synchronized on the PoolArena.
101          *
102          * See https://github.com/netty/netty/issues/3888.
103          */
104         final int defaultMinNumArena = NettyRuntime.availableProcessors() * 2;
105         final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;
106         DEFAULT_NUM_HEAP_ARENA = Math.max(0,
107                 SystemPropertyUtil.getInt(
108                         "io.netty.allocator.numHeapArenas",
109                         (int) Math.min(
110                                 defaultMinNumArena,
111                                 runtime.maxMemory() / defaultChunkSize / 2 / 3)));
112         DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
113                 SystemPropertyUtil.getInt(
114                         "io.netty.allocator.numDirectArenas",
115                         (int) Math.min(
116                                 defaultMinNumArena,
117                                 PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));
118 
119         // cache sizes
120         DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
121         DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);
122 
123         // 32 kb is the default maximum capacity of the cached buffer. Similar to what is explained in
124         // 'Scalable memory allocation using jemalloc'
125         DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
126                 "io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
127 
128         // the number of threshold of allocations when cached entries will be freed up if not frequently used
129         DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt(
130                 "io.netty.allocator.cacheTrimInterval", 8192);
131 
132         if (SystemPropertyUtil.contains("io.netty.allocation.cacheTrimIntervalMillis")) {
133             logger.warn("-Dio.netty.allocation.cacheTrimIntervalMillis is deprecated," +
134                     " use -Dio.netty.allocator.cacheTrimIntervalMillis");
135 
136             if (SystemPropertyUtil.contains("io.netty.allocator.cacheTrimIntervalMillis")) {
137                 // Both system properties are specified. Use the non-deprecated one.
138                 DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong(
139                         "io.netty.allocator.cacheTrimIntervalMillis", 0);
140             } else {
141                 DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong(
142                         "io.netty.allocation.cacheTrimIntervalMillis", 0);
143             }
144         } else {
145             DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong(
146                     "io.netty.allocator.cacheTrimIntervalMillis", 0);
147         }
148 
149         DEFAULT_USE_CACHE_FOR_ALL_THREADS = SystemPropertyUtil.getBoolean(
150                 "io.netty.allocator.useCacheForAllThreads", false);
151 
152         DEFAULT_DISABLE_CACHE_FINALIZERS_FOR_FAST_THREAD_LOCAL_THREADS = SystemPropertyUtil.getBoolean(
153                 "io.netty.allocator.disableCacheFinalizersForFastThreadLocalThreads", false);
154 
155         // Use 1023 by default as we use an ArrayDeque as backing storage which will then allocate an internal array
156         // of 1024 elements. Otherwise we would allocate 2048 and only use 1024 which is wasteful.
157         DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK = SystemPropertyUtil.getInt(
158                 "io.netty.allocator.maxCachedByteBuffersPerChunk", 1023);
159 
160         if (logger.isDebugEnabled()) {
161             logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA);
162             logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);
163             if (pageSizeFallbackCause == null) {
164                 logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE);
165             } else {
166                 logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause);
167             }
168             if (maxOrderFallbackCause == null) {
169                 logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER);
170             } else {
171                 logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause);
172             }
173             logger.debug("-Dio.netty.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER);
174             logger.debug("-Dio.netty.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE);
175             logger.debug("-Dio.netty.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE);
176             logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY);
177             logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL);
178             logger.debug("-Dio.netty.allocator.cacheTrimIntervalMillis: {}", DEFAULT_CACHE_TRIM_INTERVAL_MILLIS);
179             logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS);
180             logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}",
181                     DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK);
182             logger.debug("-Dio.netty.allocator.disableCacheFinalizersForFastThreadLocalThreads: {}",
183                          DEFAULT_DISABLE_CACHE_FINALIZERS_FOR_FAST_THREAD_LOCAL_THREADS);
184         }
185     }
186 
187     public static final PooledByteBufAllocator DEFAULT =
188             new PooledByteBufAllocator(!PlatformDependent.isExplicitNoPreferDirect());
189 
190     private final PoolArena<byte[]>[] heapArenas;
191     private final PoolArena<ByteBuffer>[] directArenas;
192     private final int smallCacheSize;
193     private final int normalCacheSize;
194     private final List<PoolArenaMetric> heapArenaMetrics;
195     private final List<PoolArenaMetric> directArenaMetrics;
196     private final PoolThreadLocalCache threadCache;
197     private final int chunkSize;
198     private final PooledByteBufAllocatorMetric metric;
199 
200     public PooledByteBufAllocator() {
201         this(false);
202     }
203 
204     @SuppressWarnings("deprecation")
205     public PooledByteBufAllocator(boolean preferDirect) {
206         this(preferDirect, DEFAULT_NUM_HEAP_ARENA, DEFAULT_NUM_DIRECT_ARENA, DEFAULT_PAGE_SIZE, DEFAULT_MAX_ORDER);
207     }
208 
209     @SuppressWarnings("deprecation")
210     public PooledByteBufAllocator(int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
211         this(false, nHeapArena, nDirectArena, pageSize, maxOrder);
212     }
213 
214     /**
215      * @deprecated use
216      * {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int, int, int, int, boolean)}
217      */
218     @Deprecated
219     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
220         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
221              0, DEFAULT_SMALL_CACHE_SIZE, DEFAULT_NORMAL_CACHE_SIZE);
222     }
223 
224     /**
225      * @deprecated use
226      * {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int, int, int, int, boolean)}
227      */
228     @Deprecated
229     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
230                                   int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
231         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder, smallCacheSize,
232              normalCacheSize, DEFAULT_USE_CACHE_FOR_ALL_THREADS, DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT);
233     }
234 
235     /**
236      * @deprecated use
237      * {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int, int, int, int, boolean)}
238      */
239     @Deprecated
240     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena,
241                                   int nDirectArena, int pageSize, int maxOrder, int tinyCacheSize,
242                                   int smallCacheSize, int normalCacheSize,
243                                   boolean useCacheForAllThreads) {
244         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
245              smallCacheSize, normalCacheSize,
246              useCacheForAllThreads);
247     }
248 
249     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena,
250                                   int nDirectArena, int pageSize, int maxOrder,
251                                   int smallCacheSize, int normalCacheSize,
252                                   boolean useCacheForAllThreads) {
253         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
254              smallCacheSize, normalCacheSize,
255              useCacheForAllThreads, DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT);
256     }
257 
258     /**
259      * @deprecated use
260      * {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int, int, int, int, boolean, int)}
261      */
262     @Deprecated
263     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
264                                   int tinyCacheSize, int smallCacheSize, int normalCacheSize,
265                                   boolean useCacheForAllThreads, int directMemoryCacheAlignment) {
266         this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
267              smallCacheSize, normalCacheSize,
268              useCacheForAllThreads, directMemoryCacheAlignment);
269     }
270 
271     public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
272                                   int smallCacheSize, int normalCacheSize,
273                                   boolean useCacheForAllThreads, int directMemoryCacheAlignment) {
274         super(preferDirect);
275         if (PlatformDependent.isJfrEnabled()) {
276             JfrEventRegistration.init();
277         }
278         threadCache = new PoolThreadLocalCache(useCacheForAllThreads);
279         this.smallCacheSize = smallCacheSize;
280         this.normalCacheSize = normalCacheSize;
281 
282         if (directMemoryCacheAlignment != 0) {
283             if (!PlatformDependent.hasAlignDirectByteBuffer()) {
284                 throw new UnsupportedOperationException("Buffer alignment is not supported. " +
285                         "Either Unsafe or ByteBuffer.alignSlice() must be available.");
286             }
287 
288             // Ensure page size is a whole multiple of the alignment, or bump it to the next whole multiple.
289             pageSize = (int) PlatformDependent.align(pageSize, directMemoryCacheAlignment);
290         }
291 
292         chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder);
293 
294         checkPositiveOrZero(nHeapArena, "nHeapArena");
295         checkPositiveOrZero(nDirectArena, "nDirectArena");
296 
297         checkPositiveOrZero(directMemoryCacheAlignment, "directMemoryCacheAlignment");
298         if (directMemoryCacheAlignment > 0 && !isDirectMemoryCacheAlignmentSupported()) {
299             throw new IllegalArgumentException("directMemoryCacheAlignment is not supported");
300         }
301 
302         if ((directMemoryCacheAlignment & -directMemoryCacheAlignment) != directMemoryCacheAlignment) {
303             throw new IllegalArgumentException("directMemoryCacheAlignment: "
304                     + directMemoryCacheAlignment + " (expected: power of two)");
305         }
306 
307         int pageShifts = validateAndCalculatePageShifts(pageSize, directMemoryCacheAlignment);
308 
309         if (nHeapArena > 0) {
310             heapArenas = newArenaArray(nHeapArena);
311             List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(heapArenas.length);
312             final SizeClasses sizeClasses = new SizeClasses(pageSize, pageShifts, chunkSize, 0);
313             for (int i = 0; i < heapArenas.length; i ++) {
314                 PoolArena.HeapArena arena = new PoolArena.HeapArena(this, sizeClasses);
315                 heapArenas[i] = arena;
316                 metrics.add(arena);
317             }
318             heapArenaMetrics = Collections.unmodifiableList(metrics);
319         } else {
320             heapArenas = null;
321             heapArenaMetrics = Collections.emptyList();
322         }
323 
324         if (nDirectArena > 0) {
325             directArenas = newArenaArray(nDirectArena);
326             List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(directArenas.length);
327             final SizeClasses sizeClasses = new SizeClasses(pageSize, pageShifts, chunkSize,
328                     directMemoryCacheAlignment);
329             for (int i = 0; i < directArenas.length; i ++) {
330                 PoolArena.DirectArena arena = new PoolArena.DirectArena(this, sizeClasses);
331                 directArenas[i] = arena;
332                 metrics.add(arena);
333             }
334             directArenaMetrics = Collections.unmodifiableList(metrics);
335         } else {
336             directArenas = null;
337             directArenaMetrics = Collections.emptyList();
338         }
339         metric = new PooledByteBufAllocatorMetric(this);
340     }
341 
342     @SuppressWarnings("unchecked")
343     private static <T> PoolArena<T>[] newArenaArray(int size) {
344         return new PoolArena[size];
345     }
346 
347     private static int validateAndCalculatePageShifts(int pageSize, int alignment) {
348         if (pageSize < MIN_PAGE_SIZE) {
349             throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: " + MIN_PAGE_SIZE + ')');
350         }
351 
352         if ((pageSize & pageSize - 1) != 0) {
353             throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: power of 2)");
354         }
355 
356         if (pageSize < alignment) {
357             throw new IllegalArgumentException("Alignment cannot be greater than page size. " +
358                     "Alignment: " + alignment + ", page size: " + pageSize + '.');
359         }
360 
361         // Logarithm base 2. At this point we know that pageSize is a power of two.
362         return Integer.SIZE - 1 - Integer.numberOfLeadingZeros(pageSize);
363     }
364 
365     private static int validateAndCalculateChunkSize(int pageSize, int maxOrder) {
366         if (maxOrder < 0 || maxOrder > 14) {
367             throw new IllegalArgumentException("maxOrder: " + maxOrder + " (expected: 0-14)");
368         }
369 
370         // Ensure the resulting chunkSize does not overflow.
371         int chunkSize = pageSize;
372         for (int i = maxOrder; i > 0; i --) {
373             if (chunkSize > MAX_CHUNK_SIZE / 2) {
374                 throw new IllegalArgumentException(String.format(
375                         "pageSize (%d) << maxOrder (%d) must not exceed %d", pageSize, maxOrder, MAX_CHUNK_SIZE));
376             }
377             chunkSize <<= 1;
378         }
379         return chunkSize;
380     }
381 
382     @Override
383     protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
384         PoolThreadCache cache = threadCache.get();
385         PoolArena<byte[]> heapArena = cache.heapArena;
386 
387         final AbstractByteBuf buf;
388         if (heapArena != null) {
389             buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
390         } else {
391             buf = PlatformDependent.hasUnsafe() ?
392                     new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) :
393                     new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
394             onAllocateBuffer(buf, false, false);
395         }
396         return toLeakAwareBuffer(buf);
397     }
398 
399     @Override
400     protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
401         PoolThreadCache cache = threadCache.get();
402         PoolArena<ByteBuffer> directArena = cache.directArena;
403 
404         final AbstractByteBuf buf;
405         if (directArena != null) {
406             buf = directArena.allocate(cache, initialCapacity, maxCapacity);
407         } else {
408             buf = UnsafeByteBufUtil.newDirectByteBuf(this, initialCapacity, maxCapacity);
409             onAllocateBuffer(buf, false, false);
410         }
411         return toLeakAwareBuffer(buf);
412     }
413 
414     /**
415      * Default number of heap arenas - System Property: io.netty.allocator.numHeapArenas - default 2 * cores
416      */
417     public static int defaultNumHeapArena() {
418         return DEFAULT_NUM_HEAP_ARENA;
419     }
420 
421     /**
422      * Default number of direct arenas - System Property: io.netty.allocator.numDirectArenas - default 2 * cores
423      */
424     public static int defaultNumDirectArena() {
425         return DEFAULT_NUM_DIRECT_ARENA;
426     }
427 
428     /**
429      * Default buffer page size - System Property: io.netty.allocator.pageSize - default 8192
430      */
431     public static int defaultPageSize() {
432         return DEFAULT_PAGE_SIZE;
433     }
434 
435     /**
436      * Default maximum order - System Property: io.netty.allocator.maxOrder - default 9
437      */
438     public static int defaultMaxOrder() {
439         return DEFAULT_MAX_ORDER;
440     }
441 
442     /**
443      * Default control creation of PoolThreadCache finalizers for FastThreadLocalThreads -
444      * System Property: io.netty.allocator.disableCacheFinalizersForFastThreadLocalThreads - default false
445      */
446     public static boolean defaultDisableCacheFinalizersForFastThreadLocalThreads() {
447         return DEFAULT_DISABLE_CACHE_FINALIZERS_FOR_FAST_THREAD_LOCAL_THREADS;
448     }
449 
450     /**
451      * Default thread caching behavior - System Property: io.netty.allocator.useCacheForAllThreads - default false
452      */
453     public static boolean defaultUseCacheForAllThreads() {
454         return DEFAULT_USE_CACHE_FOR_ALL_THREADS;
455     }
456 
457     /**
458      * Default prefer direct - System Property: io.netty.noPreferDirect - default false
459      */
460     public static boolean defaultPreferDirect() {
461         return PlatformDependent.directBufferPreferred();
462     }
463 
464     /**
465      * Default tiny cache size - default 0
466      *
467      * @deprecated Tiny caches have been merged into small caches.
468      */
469     @Deprecated
470     public static int defaultTinyCacheSize() {
471         return 0;
472     }
473 
474     /**
475      * Default small cache size - System Property: io.netty.allocator.smallCacheSize - default 256
476      */
477     public static int defaultSmallCacheSize() {
478         return DEFAULT_SMALL_CACHE_SIZE;
479     }
480 
481     /**
482      * Default normal cache size - System Property: io.netty.allocator.normalCacheSize - default 64
483      */
484     public static int defaultNormalCacheSize() {
485         return DEFAULT_NORMAL_CACHE_SIZE;
486     }
487 
488     /**
489      * Return {@code true} if direct memory cache alignment is supported, {@code false} otherwise.
490      */
491     public static boolean isDirectMemoryCacheAlignmentSupported() {
492         return PlatformDependent.hasUnsafe();
493     }
494 
495     @Override
496     public boolean isDirectBufferPooled() {
497         return directArenas != null;
498     }
499 
500     /**
501      * @deprecated will be removed
502      * Returns {@code true} if the calling {@link Thread} has a {@link ThreadLocal} cache for the allocated
503      * buffers.
504      */
505     @Deprecated
506     public boolean hasThreadLocalCache() {
507         return threadCache.isSet();
508     }
509 
510     /**
511      * @deprecated will be removed
512      * Free all cached buffers for the calling {@link Thread}.
513      */
514     @Deprecated
515     public void freeThreadLocalCache() {
516         threadCache.remove();
517     }
518 
519     private final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
520         private final boolean useCacheForAllThreads;
521 
522         PoolThreadLocalCache(boolean useCacheForAllThreads) {
523             this.useCacheForAllThreads = useCacheForAllThreads;
524         }
525 
526         @Override
527         protected synchronized PoolThreadCache initialValue() {
528             final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
529             final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
530 
531             final Thread current = Thread.currentThread();
532             final EventExecutor executor = ThreadExecutorMap.currentExecutor();
533 
534             if (useCacheForAllThreads ||
535                     // If the current thread is a FastThreadLocalThread we will always use the cache
536                     FastThreadLocalThread.currentThreadHasFastThreadLocal() ||
537                     // The Thread is used by an EventExecutor, let's use the cache as the chances are good that we
538                     // will allocate a lot!
539                     executor != null) {
540                 final PoolThreadCache cache = new PoolThreadCache(
541                         heapArena, directArena, smallCacheSize, normalCacheSize,
542                         DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL, useCacheFinalizers());
543 
544                 if (DEFAULT_CACHE_TRIM_INTERVAL_MILLIS > 0) {
545                     if (executor != null) {
546                         executor.scheduleAtFixedRate(trimTask, DEFAULT_CACHE_TRIM_INTERVAL_MILLIS,
547                                 DEFAULT_CACHE_TRIM_INTERVAL_MILLIS, TimeUnit.MILLISECONDS);
548                     }
549                 }
550                 return cache;
551             }
552             // No caching so just use 0 as sizes.
553             return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0, false);
554         }
555 
556         @Override
557         protected void onRemoval(PoolThreadCache threadCache) {
558             threadCache.free(false);
559         }
560 
561         private <T> PoolArena<T> leastUsedArena(PoolArena<T>[] arenas) {
562             if (arenas == null || arenas.length == 0) {
563                 return null;
564             }
565 
566             PoolArena<T> minArena = arenas[0];
567             //optimized
568             //If it is the first execution, directly return minarena and reduce the number of for loop comparisons below
569             if (minArena.numThreadCaches.get() == CACHE_NOT_USED) {
570                 return minArena;
571             }
572             for (int i = 1; i < arenas.length; i++) {
573                 PoolArena<T> arena = arenas[i];
574                 if (arena.numThreadCaches.get() < minArena.numThreadCaches.get()) {
575                     minArena = arena;
576                 }
577             }
578 
579             return minArena;
580         }
581     }
582 
583     private static boolean useCacheFinalizers() {
584         if (!defaultDisableCacheFinalizersForFastThreadLocalThreads()) {
585             return true;
586         }
587         return FastThreadLocalThread.currentThreadWillCleanupFastThreadLocals();
588     }
589 
590     @Override
591     public PooledByteBufAllocatorMetric metric() {
592         return metric;
593     }
594 
595     /**
596      * Return the number of heap arenas.
597      *
598      * @deprecated use {@link PooledByteBufAllocatorMetric#numHeapArenas()}.
599      */
600     @Deprecated
601     public int numHeapArenas() {
602         return heapArenaMetrics.size();
603     }
604 
605     /**
606      * Return the number of direct arenas.
607      *
608      * @deprecated use {@link PooledByteBufAllocatorMetric#numDirectArenas()}.
609      */
610     @Deprecated
611     public int numDirectArenas() {
612         return directArenaMetrics.size();
613     }
614 
615     /**
616      * Return a {@link List} of all heap {@link PoolArenaMetric}s that are provided by this pool.
617      *
618      * @deprecated use {@link PooledByteBufAllocatorMetric#heapArenas()}.
619      */
620     @Deprecated
621     public List<PoolArenaMetric> heapArenas() {
622         return heapArenaMetrics;
623     }
624 
625     /**
626      * Return a {@link List} of all direct {@link PoolArenaMetric}s that are provided by this pool.
627      *
628      * @deprecated use {@link PooledByteBufAllocatorMetric#directArenas()}.
629      */
630     @Deprecated
631     public List<PoolArenaMetric> directArenas() {
632         return directArenaMetrics;
633     }
634 
635     /**
636      * Return the number of thread local caches used by this {@link PooledByteBufAllocator}.
637      *
638      * @deprecated use {@link PooledByteBufAllocatorMetric#numThreadLocalCaches()}.
639      */
640     @Deprecated
641     public int numThreadLocalCaches() {
642         return Math.max(numThreadLocalCaches(heapArenas), numThreadLocalCaches(directArenas));
643     }
644 
645     private static int numThreadLocalCaches(PoolArena<?>[] arenas) {
646         if (arenas == null) {
647             return 0;
648         }
649 
650         int total = 0;
651         for (PoolArena<?> arena : arenas) {
652             total += arena.numThreadCaches.get();
653         }
654 
655         return total;
656     }
657 
658     /**
659      * Return the size of the tiny cache.
660      *
661      * @deprecated use {@link PooledByteBufAllocatorMetric#tinyCacheSize()}.
662      */
663     @Deprecated
664     public int tinyCacheSize() {
665         return 0;
666     }
667 
668     /**
669      * Return the size of the small cache.
670      *
671      * @deprecated use {@link PooledByteBufAllocatorMetric#smallCacheSize()}.
672      */
673     @Deprecated
674     public int smallCacheSize() {
675         return smallCacheSize;
676     }
677 
678     /**
679      * Return the size of the normal cache.
680      *
681      * @deprecated use {@link PooledByteBufAllocatorMetric#normalCacheSize()}.
682      */
683     @Deprecated
684     public int normalCacheSize() {
685         return normalCacheSize;
686     }
687 
688     /**
689      * Return the chunk size for an arena.
690      *
691      * @deprecated use {@link PooledByteBufAllocatorMetric#chunkSize()}.
692      */
693     @Deprecated
694     public final int chunkSize() {
695         return chunkSize;
696     }
697 
698     final long usedHeapMemory() {
699         return usedMemory(heapArenas);
700     }
701 
702     final long usedDirectMemory() {
703         return usedMemory(directArenas);
704     }
705 
706     private static long usedMemory(PoolArena<?>[] arenas) {
707         if (arenas == null) {
708             return -1;
709         }
710         long used = 0;
711         for (PoolArena<?> arena : arenas) {
712             used += arena.numActiveBytes();
713             if (used < 0) {
714                 return Long.MAX_VALUE;
715             }
716         }
717         return used;
718     }
719 
720     /**
721      * Returns the number of bytes of heap memory that is currently pinned to heap buffers allocated by a
722      * {@link ByteBufAllocator}, or {@code -1} if unknown.
723      * A buffer can pin more memory than its {@linkplain ByteBuf#capacity() capacity} might indicate,
724      * due to implementation details of the allocator.
725      */
726     public final long pinnedHeapMemory() {
727         return pinnedMemory(heapArenas);
728     }
729 
730     /**
731      * Returns the number of bytes of direct memory that is currently pinned to direct buffers allocated by a
732      * {@link ByteBufAllocator}, or {@code -1} if unknown.
733      * A buffer can pin more memory than its {@linkplain ByteBuf#capacity() capacity} might indicate,
734      * due to implementation details of the allocator.
735      */
736     public final long pinnedDirectMemory() {
737         return pinnedMemory(directArenas);
738     }
739 
740     private static long pinnedMemory(PoolArena<?>[] arenas) {
741         if (arenas == null) {
742             return -1;
743         }
744         long used = 0;
745         for (PoolArena<?> arena : arenas) {
746             used += arena.numPinnedBytes();
747             if (used < 0) {
748                 return Long.MAX_VALUE;
749             }
750         }
751         return used;
752     }
753 
754     final PoolThreadCache threadCache() {
755         PoolThreadCache cache =  threadCache.get();
756         assert cache != null;
757         return cache;
758     }
759 
760     /**
761      * Trim thread local cache for the current {@link Thread}, which will give back any cached memory that was not
762      * allocated frequently since the last trim operation.
763      *
764      * Returns {@code true} if a cache for the current {@link Thread} exists and so was trimmed, false otherwise.
765      */
766     public boolean trimCurrentThreadCache() {
767         PoolThreadCache cache = threadCache.getIfExists();
768         if (cache != null) {
769             cache.trim();
770             return true;
771         }
772         return false;
773     }
774 
775     /**
776      * Returns the status of the allocator (which contains all metrics) as string. Be aware this may be expensive
777      * and so should not called too frequently.
778      */
779     public String dumpStats() {
780         int heapArenasLen = heapArenas == null ? 0 : heapArenas.length;
781         StringBuilder buf = new StringBuilder(512)
782                 .append(heapArenasLen)
783                 .append(" heap arena(s):")
784                 .append(StringUtil.NEWLINE);
785         if (heapArenasLen > 0) {
786             for (PoolArena<byte[]> a: heapArenas) {
787                 buf.append(a);
788             }
789         }
790 
791         int directArenasLen = directArenas == null ? 0 : directArenas.length;
792 
793         buf.append(directArenasLen)
794            .append(" direct arena(s):")
795            .append(StringUtil.NEWLINE);
796         if (directArenasLen > 0) {
797             for (PoolArena<ByteBuffer> a: directArenas) {
798                 buf.append(a);
799             }
800         }
801 
802         return buf.toString();
803     }
804 
805     static void onAllocateBuffer(AbstractByteBuf buf, boolean pooled, boolean threadLocal) {
806         if (PlatformDependent.isJfrEnabled() && AllocateBufferEvent.isEventEnabled()) {
807             AllocateBufferEvent event = new AllocateBufferEvent();
808             if (event.shouldCommit()) {
809                 event.fill(buf, PooledByteBufAllocator.class);
810                 event.chunkPooled = pooled;
811                 event.chunkThreadLocal = threadLocal;
812                 event.commit();
813             }
814         }
815     }
816 
817     static void onDeallocateBuffer(AbstractByteBuf buf) {
818         if (PlatformDependent.isJfrEnabled() && FreeBufferEvent.isEventEnabled()) {
819             FreeBufferEvent event = new FreeBufferEvent();
820             if (event.shouldCommit()) {
821                 event.fill(buf, PooledByteBufAllocator.class);
822                 event.commit();
823             }
824         }
825     }
826 
827     static void onReallocateBuffer(AbstractByteBuf buf, int newCapacity) {
828         if (PlatformDependent.isJfrEnabled() && ReallocateBufferEvent.isEventEnabled()) {
829             ReallocateBufferEvent event = new ReallocateBufferEvent();
830             if (event.shouldCommit()) {
831                 event.fill(buf, PooledByteBufAllocator.class);
832                 event.newCapacity = newCapacity;
833                 event.commit();
834             }
835         }
836     }
837 
838     static void onAllocateChunk(ChunkInfo chunk, boolean pooled) {
839         if (PlatformDependent.isJfrEnabled() && AllocateChunkEvent.isEventEnabled()) {
840             AllocateChunkEvent event = new AllocateChunkEvent();
841             if (event.shouldCommit()) {
842                 event.fill(chunk, PooledByteBufAllocator.class);
843                 event.pooled = pooled;
844                 event.threadLocal = false; // Chunks in the pooled allocator are always shared.
845                 event.commit();
846             }
847         }
848     }
849 
850     static void onDeallocateChunk(ChunkInfo chunk, boolean pooled) {
851         if (PlatformDependent.isJfrEnabled() && FreeChunkEvent.isEventEnabled()) {
852             FreeChunkEvent event = new FreeChunkEvent();
853             if (event.shouldCommit()) {
854                 event.fill(chunk, PooledByteBufAllocator.class);
855                 event.pooled = pooled;
856                 event.commit();
857             }
858         }
859     }
860 }