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 io.netty.util.internal.LongCounter;
20  import io.netty.util.internal.PlatformDependent;
21  import io.netty.util.internal.StringUtil;
22  
23  import java.nio.ByteBuffer;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.concurrent.atomic.AtomicInteger;
28  import java.util.concurrent.locks.ReentrantLock;
29  
30  import static io.netty.buffer.PoolChunk.isSubpage;
31  import static java.lang.Math.max;
32  
33  abstract class PoolArena<T> implements PoolArenaMetric {
34      private static final boolean HAS_UNSAFE = PlatformDependent.hasUnsafe();
35  
36      enum SizeClass {
37          Small,
38          Normal
39      }
40  
41      final PooledByteBufAllocator parent;
42  
43      final PoolSubpage<T>[] smallSubpagePools;
44  
45      private final PoolChunkList<T> q050;
46      private final PoolChunkList<T> q025;
47      private final PoolChunkList<T> q000;
48      private final PoolChunkList<T> qInit;
49      private final PoolChunkList<T> q075;
50      private final PoolChunkList<T> q100;
51  
52      private final List<PoolChunkListMetric> chunkListMetrics;
53  
54      // Metrics for allocations and deallocations
55      private long allocationsNormal;
56      // We need to use the LongCounter here as this is not guarded via synchronized block.
57      private final LongCounter allocationsSmall = PlatformDependent.newLongCounter();
58      private final LongCounter allocationsHuge = PlatformDependent.newLongCounter();
59      private final LongCounter activeBytesHuge = PlatformDependent.newLongCounter();
60  
61      private long deallocationsSmall;
62      private long deallocationsNormal;
63  
64      // We need to use the LongCounter here as this is not guarded via synchronized block.
65      private final LongCounter deallocationsHuge = PlatformDependent.newLongCounter();
66  
67      // Number of thread caches backed by this arena.
68      final AtomicInteger numThreadCaches = new AtomicInteger();
69  
70      // TODO: Test if adding padding helps under contention
71      //private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
72  
73      private final ReentrantLock lock = new ReentrantLock();
74  
75      final SizeClasses sizeClass;
76  
77      protected PoolArena(PooledByteBufAllocator parent, SizeClasses sizeClass) {
78          assert null != sizeClass;
79          this.parent = parent;
80          this.sizeClass = sizeClass;
81          smallSubpagePools = newSubpagePoolArray(sizeClass.nSubpages);
82          for (int i = 0; i < smallSubpagePools.length; i ++) {
83              smallSubpagePools[i] = newSubpagePoolHead(i);
84          }
85  
86          q100 = new PoolChunkList<T>(this, null, 100, Integer.MAX_VALUE, sizeClass.chunkSize);
87          q075 = new PoolChunkList<T>(this, q100, 75, 100, sizeClass.chunkSize);
88          q050 = new PoolChunkList<T>(this, q075, 50, 100, sizeClass.chunkSize);
89          q025 = new PoolChunkList<T>(this, q050, 25, 75, sizeClass.chunkSize);
90          q000 = new PoolChunkList<T>(this, q025, 1, 50, sizeClass.chunkSize);
91          qInit = new PoolChunkList<T>(this, q000, Integer.MIN_VALUE, 25, sizeClass.chunkSize);
92  
93          q100.prevList(q075);
94          q075.prevList(q050);
95          q050.prevList(q025);
96          q025.prevList(q000);
97          q000.prevList(null);
98          qInit.prevList(qInit);
99  
100         List<PoolChunkListMetric> metrics = new ArrayList<PoolChunkListMetric>(6);
101         metrics.add(qInit);
102         metrics.add(q000);
103         metrics.add(q025);
104         metrics.add(q050);
105         metrics.add(q075);
106         metrics.add(q100);
107         chunkListMetrics = Collections.unmodifiableList(metrics);
108     }
109 
110     private PoolSubpage<T> newSubpagePoolHead(int index) {
111         PoolSubpage<T> head = new PoolSubpage<T>(index);
112         head.prev = head;
113         head.next = head;
114         return head;
115     }
116 
117     @SuppressWarnings("unchecked")
118     private PoolSubpage<T>[] newSubpagePoolArray(int size) {
119         return new PoolSubpage[size];
120     }
121 
122     abstract boolean isDirect();
123 
124     PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
125         PooledByteBuf<T> buf = newByteBuf(maxCapacity);
126         allocate(cache, buf, reqCapacity);
127         return buf;
128     }
129 
130     private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
131         final int sizeIdx = sizeClass.size2SizeIdx(reqCapacity);
132 
133         if (sizeIdx <= sizeClass.smallMaxSizeIdx) {
134             tcacheAllocateSmall(cache, buf, reqCapacity, sizeIdx);
135         } else if (sizeIdx < sizeClass.nSizes) {
136             tcacheAllocateNormal(cache, buf, reqCapacity, sizeIdx);
137         } else {
138             int normCapacity = sizeClass.directMemoryCacheAlignment > 0
139                     ? sizeClass.normalizeSize(reqCapacity) : reqCapacity;
140             // Huge allocations are never served via the cache so just call allocateHuge
141             allocateHuge(buf, normCapacity);
142         }
143     }
144 
145     private void tcacheAllocateSmall(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity,
146                                      final int sizeIdx) {
147 
148         if (cache.allocateSmall(this, buf, reqCapacity, sizeIdx)) {
149             // was able to allocate out of the cache so move on
150             return;
151         }
152 
153         /*
154          * Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
155          * {@link PoolChunk#free(long)} may modify the doubly linked list as well.
156          */
157         final PoolSubpage<T> head = smallSubpagePools[sizeIdx];
158         final boolean needsNormalAllocation;
159         head.lock();
160         try {
161             final PoolSubpage<T> s = head.next;
162             needsNormalAllocation = s == head;
163             if (!needsNormalAllocation) {
164                 assert s.doNotDestroy && s.elemSize == sizeClass.sizeIdx2size(sizeIdx) : "doNotDestroy=" +
165                         s.doNotDestroy + ", elemSize=" + s.elemSize + ", sizeIdx=" + sizeIdx;
166                 long handle = s.allocate();
167                 assert handle >= 0;
168                 s.chunk.initBufWithSubpage(buf, null, handle, reqCapacity, cache);
169             }
170         } finally {
171             head.unlock();
172         }
173 
174         if (needsNormalAllocation) {
175             lock();
176             try {
177                 allocateNormal(buf, reqCapacity, sizeIdx, cache);
178             } finally {
179                 unlock();
180             }
181         }
182 
183         incSmallAllocation();
184     }
185 
186     private void tcacheAllocateNormal(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity,
187                                       final int sizeIdx) {
188         if (cache.allocateNormal(this, buf, reqCapacity, sizeIdx)) {
189             // was able to allocate out of the cache so move on
190             return;
191         }
192         lock();
193         try {
194             allocateNormal(buf, reqCapacity, sizeIdx, cache);
195             ++allocationsNormal;
196         } finally {
197             unlock();
198         }
199     }
200 
201     private void allocateNormal(PooledByteBuf<T> buf, int reqCapacity, int sizeIdx, PoolThreadCache threadCache) {
202         assert lock.isHeldByCurrentThread();
203         if (q050.allocate(buf, reqCapacity, sizeIdx, threadCache) ||
204             q025.allocate(buf, reqCapacity, sizeIdx, threadCache) ||
205             q000.allocate(buf, reqCapacity, sizeIdx, threadCache) ||
206             qInit.allocate(buf, reqCapacity, sizeIdx, threadCache) ||
207             q075.allocate(buf, reqCapacity, sizeIdx, threadCache)) {
208             return;
209         }
210 
211         // Add a new chunk.
212         PoolChunk<T> c = newChunk(sizeClass.pageSize, sizeClass.nPSizes, sizeClass.pageShifts, sizeClass.chunkSize);
213         boolean success = c.allocate(buf, reqCapacity, sizeIdx, threadCache);
214         assert success;
215         qInit.add(c);
216     }
217 
218     private void incSmallAllocation() {
219         allocationsSmall.increment();
220     }
221 
222     private void allocateHuge(PooledByteBuf<T> buf, int reqCapacity) {
223         PoolChunk<T> chunk = newUnpooledChunk(reqCapacity);
224         activeBytesHuge.add(chunk.chunkSize());
225         buf.initUnpooled(chunk, reqCapacity);
226         allocationsHuge.increment();
227     }
228 
229     void free(PoolChunk<T> chunk, ByteBuffer nioBuffer, long handle, int normCapacity, PoolThreadCache cache) {
230         chunk.decrementPinnedMemory(normCapacity);
231         if (chunk.unpooled) {
232             int size = chunk.chunkSize();
233             destroyChunk(chunk);
234             activeBytesHuge.add(-size);
235             deallocationsHuge.increment();
236         } else {
237             SizeClass sizeClass = sizeClass(handle);
238             if (cache != null && cache.add(this, chunk, nioBuffer, handle, normCapacity, sizeClass)) {
239                 // cached so not free it.
240                 return;
241             }
242 
243             freeChunk(chunk, handle, normCapacity, sizeClass, nioBuffer, false);
244         }
245     }
246 
247     private static SizeClass sizeClass(long handle) {
248         return isSubpage(handle) ? SizeClass.Small : SizeClass.Normal;
249     }
250 
251     void freeChunk(PoolChunk<T> chunk, long handle, int normCapacity, SizeClass sizeClass, ByteBuffer nioBuffer,
252                    boolean finalizer) {
253         final boolean destroyChunk;
254         lock();
255         try {
256             // We only call this if freeChunk is not called because of the PoolThreadCache finalizer as otherwise this
257             // may fail due lazy class-loading in for example tomcat.
258             if (!finalizer) {
259                 switch (sizeClass) {
260                     case Normal:
261                         ++deallocationsNormal;
262                         break;
263                     case Small:
264                         ++deallocationsSmall;
265                         break;
266                     default:
267                         throw new Error();
268                 }
269             }
270             destroyChunk = !chunk.parent.free(chunk, handle, normCapacity, nioBuffer);
271         } finally {
272             unlock();
273         }
274         if (destroyChunk) {
275             // destroyChunk not need to be called while holding the synchronized lock.
276             destroyChunk(chunk);
277         }
278     }
279 
280     void reallocate(PooledByteBuf<T> buf, int newCapacity) {
281         assert newCapacity >= 0 && newCapacity <= buf.maxCapacity();
282 
283         final int oldCapacity;
284         final PoolChunk<T> oldChunk;
285         final ByteBuffer oldNioBuffer;
286         final long oldHandle;
287         final T oldMemory;
288         final int oldOffset;
289         final int oldMaxLength;
290         final PoolThreadCache oldCache;
291 
292         // We synchronize on the ByteBuf itself to ensure there is no "concurrent" reallocations for the same buffer.
293         // We do this to ensure the ByteBuf internal fields that are used to allocate / free are not accessed
294         // concurrently. This is important as otherwise we might end up corrupting our internal state of our data
295         // structures.
296         //
297         // Also note we don't use a Lock here but just synchronized even tho this might seem like a bad choice for Loom.
298         // This is done to minimize the overhead per ByteBuf. The time this would block another thread should be
299         // relative small and so not be a problem for Loom.
300         // See https://github.com/netty/netty/issues/13467
301         synchronized (buf) {
302             oldCapacity = buf.length;
303             if (oldCapacity == newCapacity) {
304                 return;
305             }
306 
307             oldChunk = buf.chunk;
308             oldNioBuffer = buf.tmpNioBuf;
309             oldHandle = buf.handle;
310             oldMemory = buf.memory;
311             oldOffset = buf.offset;
312             oldMaxLength = buf.maxLength;
313             oldCache = buf.cache;
314 
315             // This does not touch buf's reader/writer indices
316             allocate(parent.threadCache(), buf, newCapacity);
317         }
318         int bytesToCopy;
319         if (newCapacity > oldCapacity) {
320             bytesToCopy = oldCapacity;
321         } else {
322             buf.trimIndicesToCapacity(newCapacity);
323             bytesToCopy = newCapacity;
324         }
325         memoryCopy(oldMemory, oldOffset, buf, bytesToCopy);
326         free(oldChunk, oldNioBuffer, oldHandle, oldMaxLength, oldCache);
327     }
328 
329     @Override
330     public int numThreadCaches() {
331         return numThreadCaches.get();
332     }
333 
334     @Override
335     public int numTinySubpages() {
336         return 0;
337     }
338 
339     @Override
340     public int numSmallSubpages() {
341         return smallSubpagePools.length;
342     }
343 
344     @Override
345     public int numChunkLists() {
346         return chunkListMetrics.size();
347     }
348 
349     @Override
350     public List<PoolSubpageMetric> tinySubpages() {
351         return Collections.emptyList();
352     }
353 
354     @Override
355     public List<PoolSubpageMetric> smallSubpages() {
356         return subPageMetricList(smallSubpagePools);
357     }
358 
359     @Override
360     public List<PoolChunkListMetric> chunkLists() {
361         return chunkListMetrics;
362     }
363 
364     private static List<PoolSubpageMetric> subPageMetricList(PoolSubpage<?>[] pages) {
365         List<PoolSubpageMetric> metrics = new ArrayList<PoolSubpageMetric>();
366         for (PoolSubpage<?> head : pages) {
367             if (head.next == head) {
368                 continue;
369             }
370             PoolSubpage<?> s = head.next;
371             for (;;) {
372                 metrics.add(s);
373                 s = s.next;
374                 if (s == head) {
375                     break;
376                 }
377             }
378         }
379         return metrics;
380     }
381 
382     @Override
383     public long numAllocations() {
384         final long allocsNormal;
385         lock();
386         try {
387             allocsNormal = allocationsNormal;
388         } finally {
389             unlock();
390         }
391         return allocationsSmall.value() + allocsNormal + allocationsHuge.value();
392     }
393 
394     @Override
395     public long numTinyAllocations() {
396         return 0;
397     }
398 
399     @Override
400     public long numSmallAllocations() {
401         return allocationsSmall.value();
402     }
403 
404     @Override
405     public long numNormalAllocations() {
406         lock();
407         try {
408             return allocationsNormal;
409         } finally {
410             unlock();
411         }
412     }
413 
414     @Override
415     public long numDeallocations() {
416         final long deallocs;
417         lock();
418         try {
419             deallocs = deallocationsSmall + deallocationsNormal;
420         } finally {
421             unlock();
422         }
423         return deallocs + deallocationsHuge.value();
424     }
425 
426     @Override
427     public long numTinyDeallocations() {
428         return 0;
429     }
430 
431     @Override
432     public long numSmallDeallocations() {
433         lock();
434         try {
435             return deallocationsSmall;
436         } finally {
437             unlock();
438         }
439     }
440 
441     @Override
442     public long numNormalDeallocations() {
443         lock();
444         try {
445             return deallocationsNormal;
446         } finally {
447             unlock();
448         }
449     }
450 
451     @Override
452     public long numHugeAllocations() {
453         return allocationsHuge.value();
454     }
455 
456     @Override
457     public long numHugeDeallocations() {
458         return deallocationsHuge.value();
459     }
460 
461     @Override
462     public  long numActiveAllocations() {
463         long val = allocationsSmall.value() + allocationsHuge.value()
464                 - deallocationsHuge.value();
465         lock();
466         try {
467             val += allocationsNormal - (deallocationsSmall + deallocationsNormal);
468         } finally {
469             unlock();
470         }
471         return max(val, 0);
472     }
473 
474     @Override
475     public long numActiveTinyAllocations() {
476         return 0;
477     }
478 
479     @Override
480     public long numActiveSmallAllocations() {
481         return max(numSmallAllocations() - numSmallDeallocations(), 0);
482     }
483 
484     @Override
485     public long numActiveNormalAllocations() {
486         final long val;
487         lock();
488         try {
489             val = allocationsNormal - deallocationsNormal;
490         } finally {
491             unlock();
492         }
493         return max(val, 0);
494     }
495 
496     @Override
497     public long numActiveHugeAllocations() {
498         return max(numHugeAllocations() - numHugeDeallocations(), 0);
499     }
500 
501     @Override
502     public long numActiveBytes() {
503         long val = activeBytesHuge.value();
504         lock();
505         try {
506             for (int i = 0; i < chunkListMetrics.size(); i++) {
507                 for (PoolChunkMetric m: chunkListMetrics.get(i)) {
508                     val += m.chunkSize();
509                 }
510             }
511         } finally {
512             unlock();
513         }
514         return max(0, val);
515     }
516 
517     /**
518      * Return an estimate of the number of bytes that are currently pinned to buffer instances, by the arena. The
519      * pinned memory is not accessible for use by any other allocation, until the buffers using have all been released.
520      */
521     public long numPinnedBytes() {
522         long val = activeBytesHuge.value(); // Huge chunks are exact-sized for the buffers they were allocated to.
523         for (int i = 0; i < chunkListMetrics.size(); i++) {
524             for (PoolChunkMetric m: chunkListMetrics.get(i)) {
525                 val += ((PoolChunk<?>) m).pinnedBytes();
526             }
527         }
528         return max(0, val);
529     }
530 
531     protected abstract PoolChunk<T> newChunk(int pageSize, int maxPageIdx, int pageShifts, int chunkSize);
532     protected abstract PoolChunk<T> newUnpooledChunk(int capacity);
533     protected abstract PooledByteBuf<T> newByteBuf(int maxCapacity);
534     protected abstract void memoryCopy(T src, int srcOffset, PooledByteBuf<T> dst, int length);
535     protected abstract void destroyChunk(PoolChunk<T> chunk);
536 
537     @Override
538     public String toString() {
539         lock();
540         try {
541             StringBuilder buf = new StringBuilder()
542                     .append("Chunk(s) at 0~25%:")
543                     .append(StringUtil.NEWLINE)
544                     .append(qInit)
545                     .append(StringUtil.NEWLINE)
546                     .append("Chunk(s) at 0~50%:")
547                     .append(StringUtil.NEWLINE)
548                     .append(q000)
549                     .append(StringUtil.NEWLINE)
550                     .append("Chunk(s) at 25~75%:")
551                     .append(StringUtil.NEWLINE)
552                     .append(q025)
553                     .append(StringUtil.NEWLINE)
554                     .append("Chunk(s) at 50~100%:")
555                     .append(StringUtil.NEWLINE)
556                     .append(q050)
557                     .append(StringUtil.NEWLINE)
558                     .append("Chunk(s) at 75~100%:")
559                     .append(StringUtil.NEWLINE)
560                     .append(q075)
561                     .append(StringUtil.NEWLINE)
562                     .append("Chunk(s) at 100%:")
563                     .append(StringUtil.NEWLINE)
564                     .append(q100)
565                     .append(StringUtil.NEWLINE)
566                     .append("small subpages:");
567             appendPoolSubPages(buf, smallSubpagePools);
568             buf.append(StringUtil.NEWLINE);
569             return buf.toString();
570         } finally {
571             unlock();
572         }
573     }
574 
575     private static void appendPoolSubPages(StringBuilder buf, PoolSubpage<?>[] subpages) {
576         for (int i = 0; i < subpages.length; i ++) {
577             PoolSubpage<?> head = subpages[i];
578             if (head.next == head || head.next == null) {
579                 continue;
580             }
581 
582             buf.append(StringUtil.NEWLINE)
583                     .append(i)
584                     .append(": ");
585             PoolSubpage<?> s = head.next;
586             while (s != null) {
587                 buf.append(s);
588                 s = s.next;
589                 if (s == head) {
590                     break;
591                 }
592             }
593         }
594     }
595 
596     @Override
597     protected final void finalize() throws Throwable {
598         try {
599             super.finalize();
600         } finally {
601             destroyPoolSubPages(smallSubpagePools);
602             destroyPoolChunkLists(qInit, q000, q025, q050, q075, q100);
603         }
604     }
605 
606     private static void destroyPoolSubPages(PoolSubpage<?>[] pages) {
607         for (PoolSubpage<?> page : pages) {
608             page.destroy();
609         }
610     }
611 
612     private void destroyPoolChunkLists(PoolChunkList<T>... chunkLists) {
613         for (PoolChunkList<T> chunkList: chunkLists) {
614             chunkList.destroy(this);
615         }
616     }
617 
618     static final class HeapArena extends PoolArena<byte[]> {
619 
620         HeapArena(PooledByteBufAllocator parent, SizeClasses sizeClass) {
621             super(parent, sizeClass);
622         }
623 
624         private static byte[] newByteArray(int size) {
625             return PlatformDependent.allocateUninitializedArray(size);
626         }
627 
628         @Override
629         boolean isDirect() {
630             return false;
631         }
632 
633         @Override
634         protected PoolChunk<byte[]> newChunk(int pageSize, int maxPageIdx, int pageShifts, int chunkSize) {
635             return new PoolChunk<byte[]>(
636                     this, null, newByteArray(chunkSize), pageSize, pageShifts, chunkSize, maxPageIdx);
637         }
638 
639         @Override
640         protected PoolChunk<byte[]> newUnpooledChunk(int capacity) {
641             return new PoolChunk<byte[]>(this, null, newByteArray(capacity), capacity);
642         }
643 
644         @Override
645         protected void destroyChunk(PoolChunk<byte[]> chunk) {
646             // Rely on GC.
647         }
648 
649         @Override
650         protected PooledByteBuf<byte[]> newByteBuf(int maxCapacity) {
651             return HAS_UNSAFE ? PooledUnsafeHeapByteBuf.newUnsafeInstance(maxCapacity)
652                     : PooledHeapByteBuf.newInstance(maxCapacity);
653         }
654 
655         @Override
656         protected void memoryCopy(byte[] src, int srcOffset, PooledByteBuf<byte[]> dst, int length) {
657             if (length == 0) {
658                 return;
659             }
660 
661             System.arraycopy(src, srcOffset, dst.memory, dst.offset, length);
662         }
663     }
664 
665     static final class DirectArena extends PoolArena<ByteBuffer> {
666 
667         DirectArena(PooledByteBufAllocator parent, SizeClasses sizeClass) {
668             super(parent, sizeClass);
669         }
670 
671         @Override
672         boolean isDirect() {
673             return true;
674         }
675 
676         @Override
677         protected PoolChunk<ByteBuffer> newChunk(int pageSize, int maxPageIdx,
678             int pageShifts, int chunkSize) {
679             if (sizeClass.directMemoryCacheAlignment == 0) {
680                 ByteBuffer memory = allocateDirect(chunkSize);
681                 return new PoolChunk<ByteBuffer>(this, memory, memory, pageSize, pageShifts,
682                         chunkSize, maxPageIdx);
683             }
684 
685             final ByteBuffer base = allocateDirect(chunkSize + sizeClass.directMemoryCacheAlignment);
686             final ByteBuffer memory = PlatformDependent.alignDirectBuffer(base, sizeClass.directMemoryCacheAlignment);
687             return new PoolChunk<ByteBuffer>(this, base, memory, pageSize,
688                     pageShifts, chunkSize, maxPageIdx);
689         }
690 
691         @Override
692         protected PoolChunk<ByteBuffer> newUnpooledChunk(int capacity) {
693             if (sizeClass.directMemoryCacheAlignment == 0) {
694                 ByteBuffer memory = allocateDirect(capacity);
695                 return new PoolChunk<ByteBuffer>(this, memory, memory, capacity);
696             }
697 
698             final ByteBuffer base = allocateDirect(capacity + sizeClass.directMemoryCacheAlignment);
699             final ByteBuffer memory = PlatformDependent.alignDirectBuffer(base, sizeClass.directMemoryCacheAlignment);
700             return new PoolChunk<ByteBuffer>(this, base, memory, capacity);
701         }
702 
703         private static ByteBuffer allocateDirect(int capacity) {
704             return PlatformDependent.useDirectBufferNoCleaner() ?
705                     PlatformDependent.allocateDirectNoCleaner(capacity) : ByteBuffer.allocateDirect(capacity);
706         }
707 
708         @Override
709         protected void destroyChunk(PoolChunk<ByteBuffer> chunk) {
710             if (PlatformDependent.useDirectBufferNoCleaner()) {
711                 PlatformDependent.freeDirectNoCleaner((ByteBuffer) chunk.base);
712             } else {
713                 PlatformDependent.freeDirectBuffer((ByteBuffer) chunk.base);
714             }
715         }
716 
717         @Override
718         protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) {
719             if (HAS_UNSAFE) {
720                 return PooledUnsafeDirectByteBuf.newInstance(maxCapacity);
721             } else {
722                 return PooledDirectByteBuf.newInstance(maxCapacity);
723             }
724         }
725 
726         @Override
727         protected void memoryCopy(ByteBuffer src, int srcOffset, PooledByteBuf<ByteBuffer> dstBuf, int length) {
728             if (length == 0) {
729                 return;
730             }
731 
732             if (HAS_UNSAFE) {
733                 PlatformDependent.copyMemory(
734                         PlatformDependent.directBufferAddress(src) + srcOffset,
735                         PlatformDependent.directBufferAddress(dstBuf.memory) + dstBuf.offset, length);
736             } else {
737                 // We must duplicate the NIO buffers because they may be accessed by other Netty buffers.
738                 src = src.duplicate();
739                 ByteBuffer dst = dstBuf.internalNioBuffer();
740                 src.position(srcOffset).limit(srcOffset + length);
741                 dst.position(dstBuf.offset);
742                 dst.put(src);
743             }
744         }
745     }
746 
747     void lock() {
748         lock.lock();
749     }
750 
751     void unlock() {
752         lock.unlock();
753     }
754 
755     @Override
756     public int sizeIdx2size(int sizeIdx) {
757         return sizeClass.sizeIdx2size(sizeIdx);
758     }
759 
760     @Override
761     public int sizeIdx2sizeCompute(int sizeIdx) {
762         return sizeClass.sizeIdx2sizeCompute(sizeIdx);
763     }
764 
765     @Override
766     public long pageIdx2size(int pageIdx) {
767         return sizeClass.pageIdx2size(pageIdx);
768     }
769 
770     @Override
771     public long pageIdx2sizeCompute(int pageIdx) {
772         return sizeClass.pageIdx2sizeCompute(pageIdx);
773     }
774 
775     @Override
776     public int size2SizeIdx(int size) {
777         return sizeClass.size2SizeIdx(size);
778     }
779 
780     @Override
781     public int pages2pageIdx(int pages) {
782         return sizeClass.pages2pageIdx(pages);
783     }
784 
785     @Override
786     public int pages2pageIdxFloor(int pages) {
787         return sizeClass.pages2pageIdxFloor(pages);
788     }
789 
790     @Override
791     public int normalizeSize(int size) {
792         return sizeClass.normalizeSize(size);
793     }
794 }