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.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
55 private long allocationsNormal;
56
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
65 private final LongCounter deallocationsHuge = PlatformDependent.newLongCounter();
66
67
68 final AtomicInteger numThreadCaches = new AtomicInteger();
69
70
71
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
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
150 return;
151 }
152
153
154
155
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
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
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
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
257
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
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
293
294
295
296
297
298
299
300
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
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
519
520
521 public long numPinnedBytes() {
522 long val = activeBytesHuge.value();
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
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
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 }