1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
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
74
75 final Runtime runtime = Runtime.getRuntime();
76
77
78
79
80
81
82
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
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
105
106 DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
107 "io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
108
109
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
172
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
182
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
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
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
345
346 public static int defaultNumHeapArena() {
347 return DEFAULT_NUM_HEAP_ARENA;
348 }
349
350
351
352
353 public static int defaultNumDirectArena() {
354 return DEFAULT_NUM_DIRECT_ARENA;
355 }
356
357
358
359
360 public static int defaultPageSize() {
361 return DEFAULT_PAGE_SIZE;
362 }
363
364
365
366
367 public static int defaultMaxOrder() {
368 return DEFAULT_MAX_ORDER;
369 }
370
371
372
373
374 public static boolean defaultUseCacheForAllThreads() {
375 return DEFAULT_USE_CACHE_FOR_ALL_THREADS;
376 }
377
378
379
380
381 public static boolean defaultPreferDirect() {
382 return PlatformDependent.directBufferPreferred();
383 }
384
385
386
387
388 public static int defaultTinyCacheSize() {
389 return DEFAULT_TINY_CACHE_SIZE;
390 }
391
392
393
394
395 public static int defaultSmallCacheSize() {
396 return DEFAULT_SMALL_CACHE_SIZE;
397 }
398
399
400
401
402 public static int defaultNormalCacheSize() {
403 return DEFAULT_NORMAL_CACHE_SIZE;
404 }
405
406
407
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
420
421
422 @Deprecated
423 public boolean hasThreadLocalCache() {
424 return threadCache.isSet();
425 }
426
427
428
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
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
486
487
488
489 @Deprecated
490 public int numHeapArenas() {
491 return heapArenaMetrics.size();
492 }
493
494
495
496
497
498
499 @Deprecated
500 public int numDirectArenas() {
501 return directArenaMetrics.size();
502 }
503
504
505
506
507
508
509 @Deprecated
510 public List<PoolArenaMetric> heapArenas() {
511 return heapArenaMetrics;
512 }
513
514
515
516
517
518
519 @Deprecated
520 public List<PoolArenaMetric> directArenas() {
521 return directArenaMetrics;
522 }
523
524
525
526
527
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
546
547
548
549 @Deprecated
550 public int tinyCacheSize() {
551 return tinyCacheSize;
552 }
553
554
555
556
557
558
559 @Deprecated
560 public int smallCacheSize() {
561 return smallCacheSize;
562 }
563
564
565
566
567
568
569 @Deprecated
570 public int normalCacheSize() {
571 return normalCacheSize;
572 }
573
574
575
576
577
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
614
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 }