1
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;
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
87
88 final Runtime runtime = Runtime.getRuntime();
89
90
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
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
117
118 DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
119 "io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
120
121
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
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
149
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
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
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
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
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
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
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
391 public static int defaultNumHeapArena() {
392 return DEFAULT_NUM_HEAP_ARENA;
393 }
394
395
398 public static int defaultNumDirectArena() {
399 return DEFAULT_NUM_DIRECT_ARENA;
400 }
401
402
405 public static int defaultPageSize() {
406 return DEFAULT_PAGE_SIZE;
407 }
408
409
412 public static int defaultMaxOrder() {
413 return DEFAULT_MAX_ORDER;
414 }
415
416
419 public static boolean defaultUseCacheForAllThreads() {
420 return DEFAULT_USE_CACHE_FOR_ALL_THREADS;
421 }
422
423
426 public static boolean defaultPreferDirect() {
427 return PlatformDependent.directBufferPreferred();
428 }
429
430
435 @Deprecated
436 public static int defaultTinyCacheSize() {
437 return 0;
438 }
439
440
443 public static int defaultSmallCacheSize() {
444 return DEFAULT_SMALL_CACHE_SIZE;
445 }
446
447
450 public static int defaultNormalCacheSize() {
451 return DEFAULT_NORMAL_CACHE_SIZE;
452 }
453
454
457 public static boolean isDirectMemoryCacheAlignmentSupported() {
458 return PlatformDependent.hasUnsafe();
459 }
460
461 @Override
462 public boolean isDirectBufferPooled() {
463 return directArenas != null;
464 }
465
466
470 @Deprecated
471 public boolean hasThreadLocalCache() {
472 return threadCache.isSet();
473 }
474
475
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
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
546 @Deprecated
547 public int numHeapArenas() {
548 return heapArenaMetrics.size();
549 }
550
551
556 @Deprecated
557 public int numDirectArenas() {
558 return directArenaMetrics.size();
559 }
560
561
566 @Deprecated
567 public List<PoolArenaMetric> heapArenas() {
568 return heapArenaMetrics;
569 }
570
571
576 @Deprecated
577 public List<PoolArenaMetric> directArenas() {
578 return directArenaMetrics;
579 }
580
581
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
606 @Deprecated
607 public int tinyCacheSize() {
608 return 0;
609 }
610
611
616 @Deprecated
617 public int smallCacheSize() {
618 return smallCacheSize;
619 }
620
621
626 @Deprecated
627 public int normalCacheSize() {
628 return normalCacheSize;
629 }
630
631
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
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
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