hi,你好!欢迎访问本站!登录
本站由简数采集腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 杂谈 - 正文 君子好学,自强不息!

netty源码剖析(4.0)-27 ByteBuf内存池:PoolArena-PoolThreadCache,netty源码解剖析(4.0)-25 ByteBuf内存池:PoolArena-PoolChunk,netty源码解剖析(4.0)-26 ByteBuf内存池:PoolArena-PoolSubpage

2019-11-18杂谈搜奇网31°c
A+ A-

  前面两章剖析的PoolChunk和PoolSubpage,从功用上来讲已能够直接拿来用了。但直接运用这个两个类治理内存在高频分派/开释内存场景下会有机能题目,PoolChunk分派内存时算法复杂度最高的是allocateNode要领,开释内存时算法复杂度最高的是free要领。 PoolChunk中二叉树的高度是maxOrder,  那末算法担任度是O(maxOrder),netty默许的maxOrder是11。别的,PoolChunk不是线程平安的,假如在多线程环境下须要加锁挪用,这个开支比算法开支还要大。

  为了处理机能题目,netty设想PoolThreadCache(PTC)。每一个线程持有一个PTC对象,每一个PTC对象持有多个MemoryRegionCache(MRC)对象。MRC对象缓存了大小雷同的内存块。PooledByteBuf在开释内存时,会把内存缓存到,MRC对象中,下次分派内存是会优先从MRC中掏出缓存的内存。如许,在高频,多线程分派/开释的场景下,能够防止绝大部分PoolChunk算法开支和锁开支。

 

cache的设想

  在netty源码解剖析(4.0)-25 ByteBuf内存池:PoolArena-PoolChunk中讲到,PoolArena把内存按内存大小把内存分为4中范例。PTC只缓存Tiny,Small, Normal三种内存。PTC内部保护了这三种内存的缓存数组,每种内存有两个数组,离别用来缓存堆内存和直接内存。

    private final MemoryRegionCache<byte[]>[] tinySubPageHeapCaches;
    private final MemoryRegionCache<byte[]>[] smallSubPageHeapCaches;
    private final MemoryRegionCache<ByteBuffer>[] tinySubPageDirectCaches;
    private final MemoryRegionCache<ByteBuffer>[] smallSubPageDirectCaches;
    private final MemoryRegionCache<byte[]>[] normalHeapCaches;
    private final MemoryRegionCache<ByteBuffer>[] normalDirectCaches;

  这几十个数组都在PTC的组织要领中初始化,tinySubPageHeapCahes和tinSubPageDirectCaches的长度,PoolArena.numTinySubpagePools。smallSubPageHeapCaches和smallSubPageDirectCaches的长度是heapArena.numSmallSubpagePools。这个两种范例的cache都是挪用createSubPageCaches要领建立。normalHeadpCaches和normalDirectCaches的长度取决于传递给组织要领的maxCachedBufferCapacity参数和PoolArena.pageSize,这类cache是挪用createNormalCaches建立。

  PoolArena.numTinySubpagePools和PoolArena.numSmallSubpagePools的寄义在netty源码解剖析(4.0)-26 ByteBuf内存池:PoolArena-PoolSubpage中有细致的剖析。

  下面以createNormalCaches要领的完成为例剖析cache的建立:

 1     private static <T> MemoryRegionCache<T>[] createNormalCaches(
 2             int cacheSize, int maxCachedBufferCapacity, PoolArena<T> area) {
 3         if (cacheSize > 0 && maxCachedBufferCapacity > 0) {
 4             int max = Math.min(area.chunkSize, maxCachedBufferCapacity);
 5             int arraySize = Math.max(1, log2(max / area.pageSize) + 1);
 6 
 7             @SuppressWarnings("unchecked")
 8             MemoryRegionCache<T>[] cache = new MemoryRegionCache[arraySize];
 9             for (int i = 0; i < cache.length; i++) {
10                 cache[i] = new NormalMemoryRegionCache<T>(cacheSize);
11             }
12             return cache;
13         } else {
14             return null;
15         }
16     }

  和createSubPageCaches差别,这个要领没有数组长度的参数,须要本身盘算数组长度。

  4,5行,盘算cache数组长度。max是最大运转缓存的内存大小,它被限制为<=chunkSize。arraySize是数组的大小。假如max/area.pageSize = 2k, (k<=maxOrder)。log2(max/ares.pageSize) = k。arraySize 最小是1, 最大是maxOrder + 1。这意味着可缓存的内存大小是pageSize * 20, paggeSize * 21, ...... pageSize * 2arraySize-1

  8-11行,建立cache数组,并逐一初始化。

  

  这三种范例的数组有差别的特征,这些特征就是它们缓存内存的体式格局:

  tinySubPageHeapCahes和tinSubPageDirectCaches:  这两个数组的长度是512 >> 4 = 512/16 = 32。索引idx位置缓存的内存长度normCapacity = idx  * 16, 已知normCapacity,idx = normCapacity/16 = normCapacity >> 4。

  smallSubPageHeapCaches和smallSubPageDirectCaches: 这个数组的长度是log2(pageSize) - 9。索引idx位置缓存内存的长度normCapacity = (1 << 9) * 2idx =29+idx,  已知normCapacity,idx = log2(normCapacity) - 9。

  normalHeadpCaches和normalDirectCaches: 这个数组的长度局限是[1, maxOrder + 1)。索引idx位置缓存的内存长度normCapacity = pageSize * 2idx, 已知normCapacity,idx=log2(normCapacity/pageSize)。

 

向cache中增加内存

  在PooledByteBuf是不是内存时,会优挪用PTC对象的add要领先把内存添增加到cache中:

 1     boolean add(PoolArena<?> area, PoolChunk chunk, long handle, int normCapacity, SizeClass sizeClass) {
 2         MemoryRegionCache<?> cache = cache(area, normCapacity, sizeClass);
 3         if (cache == null) {
 4             return false;
 5         }
 6         return cache.add(chunk, handle);
 7     }
 8 
 9     private MemoryRegionCache<?> cache(PoolArena<?> area, int normCapacity, SizeClass sizeClass) {
10         switch (sizeClass) {
11         case Normal:
12             return cacheForNormal(area, normCapacity);
13         case Small:
14             return cacheForSmall(area, normCapacity);
15         case Tiny:
16             return cacheForTiny(area, normCapacity);
17         default:
18             throw new Error();
19         }
20     }

  2行,挪用cache要领找定位到MRC对象。

  6行,把内存增加MRC对象。

  10-19行,依据sizeClass挪用差别的要领定位MRC对象。这里的sizeClass是依据normCapacity获得的,

    normCapacity < 512: sizeClass = Tiny

    512 <= normCapacity < pageSize: sizeClass = Small

    pageSize <= normCapacity < chunkSize: sizeClass = Nomral

  接下来看看这三个用来定位MRC对象的要领是怎样完成的。起首来看cacheForTiny:

 1     private MemoryRegionCache<?> cacheForTiny(PoolArena<?> area, int normCapacity) {
 2         int idx = PoolArena.tinyIdx(normCapacity);
 3         if (area.isDirect()) {
 4             return cache(tinySubPageDirectCaches, idx);
 5         }
 6         return cache(tinySubPageHeapCaches, idx);
 7     }
 8 
 9     private static <T> MemoryRegionCache<T> cache(MemoryRegionCache<T>[] cache, int idx) {
10         if (cache == null || idx > cache.length - 1) {
11             return null;
12         }
13         return cache[idx];
14     }

  第2行, 盘算数组的索引 idx = normapCapacity >> 4。

  第4,6行挪用的cache完成代码在9-14行。把MRC对象从数组中掏出。

 

  cacheForSmall,cacheForNormal要领和cacheForTiny相似,差别的是盘算idx的要领。

 1     private MemoryRegionCache<?> cacheForSmall(PoolArena<?> area, int normCapacity) {
 2         int idx = PoolArena.smallIdx(normCapacity);
 3         if (area.isDirect()) {
 4             return cache(smallSubPageDirectCaches, idx);
 5         }
 6         return cache(smallSubPageHeapCaches, idx);
 7     }
 8 
 9     private MemoryRegionCache<?> cacheForNormal(PoolArena<?> area, int normCapacity) {
10         if (area.isDirect()) {
11             int idx = log2(normCapacity >> numShiftsNormalDirect);
12             return cache(normalDirectCaches, idx);
13         }
14         int idx = log2(normCapacity >> numShiftsNormalHeap);
15         return cache(normalHeapCaches, idx);
16     }

  第2行盘算idx要领和第11行相似: log2(val),  初始化res=0,轮回盘算(val >>> 1) == 0 ? res : res += 1。当res稳定时返回,这个是就是log2(val)的值。 

  第11行,numShiftsNormalDirect = log2(pageSize),  normCapacity >> numShiftsNormalDirect = normCapacity/pageSize。第14行同理。 

 

从cache中分派内存

  分派内存的历程也依靠前面剖析的几个cacheForXXX要领:

 1 /**
 2      * Try to allocate a tiny buffer out of the cache. Returns {@code true} if successful {@code false} otherwise
 3      */
 4     boolean allocateTiny(PoolArena<?> area, PooledByteBuf<?> buf, int reqCapacity, int normCapacity) {
 5         return allocate(cacheForTiny(area, normCapacity), buf, reqCapacity);
 6     }
 7 
 8     /**
 9      * Try to allocate a small buffer out of the cache. Returns {@code true} if successful {@code false} otherwise
10      */
11     boolean allocateSmall(PoolArena<?> area, PooledByteBuf<?> buf, int reqCapacity, int normCapacity) {
12         return allocate(cacheForSmall(area, normCapacity), buf, reqCapacity);
13     }
14 
15     /**
16      * Try to allocate a small buffer out of the cache. Returns {@code true} if successful {@code false} otherwise
17      */
18     boolean allocateNormal(PoolArena<?> area, PooledByteBuf<?> buf, int reqCapacity, int normCapacity) {
19         return allocate(cacheForNormal(area, normCapacity), buf, reqCapacity);
20     }

  allocate要领完成比较简单,它挪用MRC对象的allocate要领为PooledByteBuf分派内存,并初始化。

  

MemoryRegionCache(MRC)完成

  PTC运用MRC对象缓存大小雷同的内存块。它内部保护了一个行列,行列中保留的是大小从PoolChunk中分派的内存块。它有两个最主要的属性:

  Queue<Entry<T>> queue:  缓存内存块的行列。

  SizeClass sizeClass:  内存的范例, Tiny, Small或Normal。

  MRC有三个类:

  MemoryRegionCache<T>: 笼统类,定义了笼统要领initBuf。

  SubPageMemoryRegionCache<T>: 完成initBuf要领,运用Tiny或Small内存初始化PooledByteBuf。

  NormalMemoryRegionCache<T>: 完成initBuf要领,运用Normal内存初始化PooledByteBuf。

  MRC的主要功用是:缓存一块内存,把PoolChunk, handle代表的内存增加到queue中。从queue中掏出一块内存,挪用initBuf要领初始化PooledByteBuf。

 

缓存内存

 1         public final boolean add(PoolChunk<T> chunk, long handle) {
 2             Entry<T> entry = newEntry(chunk, handle);
 3             boolean queued = queue.offer(entry);
 4             if (!queued) {
 5                 // If it was not possible to cache the chunk, immediately recycle the entry
 6                 entry.recycle();
 7             }
 8 
 9             return queued;
10         }

  这个要领用来吧chunk和handle代表的内存增加的queue中。Entry<T>是MRC的内部类,完成很简单,只是为了能在queue中缓存chunk和handle数据,它运用了Recycler功用,把本身放进了可轮回运用的对象池中。

 

从掏出一块内存,并初始化PooledByteBuf

 1         public final boolean allocate(PooledByteBuf<T> buf, int reqCapacity) {
 2             Entry<T> entry = queue.poll();
 3             if (entry == null) {
 4                 return false;
 5             }
 6             initBuf(entry.chunk, entry.handle, buf, reqCapacity);
 7             entry.recycle();
 8 
 9             // allocations is not thread-safe which is fine as this is only called from the same thread all time.
10             ++ allocations;
11             return true;
12         }

  2-5行,掏出一块内存。

  6行,初始化PooledByteBuf。

  下面是两个initBuf完成。

 1        //SubPageMemoryRegionCache<T>
 2         @Override
 3         protected void initBuf(
 4                 PoolChunk<T> chunk, long handle, PooledByteBuf<T> buf, int reqCapacity) {
 5             chunk.initBufWithSubpage(buf, handle, reqCapacity);
 6         } 
 7     
 8         //NormalMemoryRegionCache<T>
 9         @Override
10         protected void initBuf(
11                 PoolChunk<T> chunk, long handle, PooledByteBuf<T> buf, int reqCapacity) {
12             chunk.initBuf(buf, handle, reqCapacity);
13         }    

  由5, 12行,能够看到,这两个要领只是用来挪用PoolChunk完成的PooledByteBuf初始化要领。

 

  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
netty源码剖析(4.0)-27 ByteBuf内存池:PoolArena-PoolThreadCache,netty源码解剖析(4.0)-25 ByteBuf内存池:PoolArena-PoolChunk,netty源码解剖析(4.0)-26 ByteBuf内存池:PoolArena-PoolSubpage

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
未定义标签

本文来源:搜奇网

本文地址:https://www.sou7.cn/282330.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

发表评论

选填

必填

必填

选填

请拖动滑块解锁
>>