Netty分布式ByteBuf使用subPage级别内存分配的方法


这篇文章主要介绍“Netty分布式ByteBuf使用subPage级别内存分配的方法”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Netty分布式ByteBuf使用subPage级别内存分配的方法”文章能帮助大家解决问题。简单起见, 我们这里仅仅以16字节为例, 讲解其分配逻辑在分析其逻辑前, 首先看PoolArean的一个属性:这个属性是一个PoolSubpage的数组, 有点类似于一个subpage的缓存, 我们创建一个subpage之后, 会将创建的subpage与该属性其中每个关联, 下次在分配的时候可以直接通过该属性的元素去找关联的subpage这里为numTinySubpagePools为32跟到newSubpagePoolArray(numTinySubpagePools)方法里:这里直接创建了一个PoolSubpage数组, 长度为32我们跟到newSubpagePoolHead中:这里创建了一个PoolSubpage对象head这种写法我们知道Subpage其实也是个双向链表, 这里的将head的上一个节点和下一个节点都设置为自身, 有关PoolSubpage的关联关系, 我们稍后会看到这样通过循环创建PoolSubpage, 总共会创建出32个subpage, 其中每个subpage实际代表一块内存大小:5-8-1这里就有点类之前小节的缓存数组tinySubPageDirectCaches的结构了解了tinySubpagePools属性, 我们看PoolArean的allocate方法, 也就是缓冲区的入口方法:之前我们最这个方法剖析过在page级别相关内存分配逻辑, 这一小节看subpage级别分配的相关逻辑假设我们分配16字节的缓冲区, isTinyOrSmall(normCapacity)就会返回true, 进入if块同样if (tiny)这里会返回true, 继续跟到if (tiny)中:首先会在缓存中分配缓冲区, 如果分配不到, 就开辟一块内存进行内存分配首先看这一步:我们跟进去:这里将normCapacity除以16, 其实也就是1我们回到PoolArena的allocate方法继续看:table = tinySubpagePools这里将tinySubpagePools赋值到局部变量table中, 继续往下看finalPoolSubpage head = table[tableIdx]这步时通过下标拿到一个PoolSubpage, 因为我们以16字节为例, 所以我们拿到下标为1的PoolSubpage, 对应的内存大小也就是16B再看finalPoolSubpage s = head.next这一步, 跟我们刚才了解的的tinySubpagePools属性, 默认情况下head.next也是自身, 所以if (s != head)会返回false, 我们继续往下看:下面, 会走到allocateNormal(buf, reqCapacity, normCapacity)这个方法:这里的逻辑我们之前的小节已经剖析过, 首先在原来的chunk中分配, 如果分配不成功, 则会创建chunk进行分配我们看这一步longhandle = c.allocate(normCapacity)上一小节我们分析page级别分配的时候,剖析的是allocateRun(normCapacity)方法因为这里我们是以16字节举例,所以这次我们剖析allocateSubpage(normCapacity)方法,也就是在subpage级别进行内存分配首先,通过PoolSubpage head = arena.findSubpagePoolHead(normCapacity)这种方式找到head节点,实际上这里head,就是我们刚才分析的tinySubpagePools属性的第一个节点,也就是对应16B的那个节点intd = maxOrder是将11赋值给d,也就是在内存树的第11层取节点,这部分上一小节剖析过了,可以回顾图5-8-5部分intid = allocateNode(d)这里获取的是上一小节我们分析过的,字节数组memoryMap的下标,这里指向一个page,如果第一次分配,指向的是0-8k的那个page,上一小节对此进行详细的剖析这里不再赘述finalPoolSubpage[] subpages =this.subpages这一步,是拿到PoolChunk中成员变量subpages的值,也是个PoolSubpage的数组,在PoolChunk进行初始化的时候,也会初始化该数组,长度为2048也就是说每个chunk都维护着一个subpage的列表,如果每一个page级别的内存都需要被切分成子page,则会将这个这个page放入该列表中,专门用于分配子page,所以这个列表中的subpage,其实就是一个用于切分的page5-8-2intsubpageIdx = subpageIdx(id)这一步是通过id拿到这个PoolSubpage数组的下标,如果id对应的page是0-8k的节点,这里拿到的下标就是0在if(subpage ==null)中,因为默认subpages只是创建一个数组,并没有往数组中赋值,所以第一次走到这里会返回true,跟到if块中:这里通过new PoolSubpage创建一个新的subpage之后,通过subpages[subpageIdx] = subpage这种方式将新创建的subpage根据下标赋值到subpages中的元素中在new PoolSubpage的构造方法中,传入head,就是我们刚才提到过的tinySubpagePools属性中的节点,如果我们分配的16字节的缓冲区,则这里对应的就是第一个节点这里重点关注属性bitmap,这是一个long类型的数组,初始大小为8,这里只是初始化的大小,真正的大小要根据将page切分多少块而确定这里将属性进行了赋值,我们跟到init方法中:this.elemSize = elemSize表示保存当前分配的缓冲区大小,这里我们以16字节举例,所以这里是16maxNumElems = numAvail = pageSize / elemSize这里初始化了两个属性maxNumElems, numAvail,值都为pageSize / elemSize,表示一个page大小除以分配的缓冲区大小,也就是表示当前page被划分了多少分numAvail则表示剩余可用的块数,由于第一次分配都是可用的,所以numAvail=maxNumElemsbitmapLength表示bitmap的实际大小,刚才我们分析过, bitmap初始化的大小为8,但实际上并不一定需要8个元素,元素个数要根据page切分的子块而定,这里的大小是所切分的子块数除以64再往下看,if((maxNumElems & 63) != 0)判断maxNumElems也就是当前配置所切分的子块是不是64的倍数,如果不是,则bitmapLength加1,最后通过循环,将其分配的大小中的元素赋值为0这里详细介绍一下有关bitmap,这里是个long类型的数组, long数组中的每一个值,也就是long类型的数字,其中的每一个比特位,都标记着page中每一个子块的内存是否已分配,如果比特位是1,表示该子块已分配,如果比特位是0,表示该子块未分配,标记顺序是其二进制数从低位到高位进行排列这里,我们应该知道为什么bitmap大小要设置为子块数量除以, 64,因为long类型的数字是64位,每一个元素能记录64个子块的数量,这样就可以通过子page个数除以64的方式决定bitmap中元素的数量如果子块不能整除64,则通过元素数量+1方式,除以64之后剩余的子块通过long中比特位由低到高进行排列记录这里的逻辑结构如下所示:5-8-3这里的head我们刚才讲过,是Arena中数组tinySubpagePools中的元素,通过以上逻辑,就会将新创建的Subpage通过双向链表的方式关联到tinySubpagePools中的元素,我们以16字节为例,关联关系如图所示:5-8-4这样,下次如果还需要分配16字节的内存,就可以通过tinySubpagePools找到其元素关联的subpage进行分配了我们再回到PoolChunk的allocateSubpage方法中:创建完了一个subpage,我们就可以通过subpage.allocate()方法进行内存分配了这里的逻辑看起来比较复杂,这里带着大家一点点剖析:首先看:其中bitmapIdx表示从bitmap中找到一个可用的bit位的下标,注意,这里是bit的下标,并不是数组的下标,我们之前分析过,因为每一比特位代表一个子块的内存分配情况,通过这个下标就可以知道那个比特位是未分配状态我们跟进这个方法:这里nextAvail, 表示下一个可用的bitmapIdx, 在释放的时候的会被标记,标记被释放的子块对应bitmapIdx的下标,如果
这里会遍历bitmap中的每一个元素,如果当前元素中所有的比特位并没有全部标记被使用,则通过findNextAvail0(i, bits)方法挨个往后找标记未使用的比特位再继续跟findNextAvail0:这里从当前元素的第一个比特位开始找,直到找到一个标记为0的比特位,并返回当前比特位的下标,大概流程如下图所示:5-8-5找到可用的bit免费云主机域名mapIdx之后,通过intq = bitmapIdx >>> 6获取bitmap中bitmapIdx所属元素的数组下标intr = bitmapIdx & 63表示获取bitmapIdx的位置是从当前元素最低位开始的第几个比特位bitmap[q] |= 1L
然后将可用子配置的数量numAvail减一如果没有可用子page的数量,则会将PoolArena中的数组tinySubpagePools所关联的subpage进行移除,移除之后参考图5-8-1最后通过toHandle(bitmapIdx)获取当前子块的handle,上一小节我们知道handle指向的是当前chunk中的唯一的一块内存,我们跟进toHandle(bitmapIdx)中:(long) bitmapIdx
0x4000000000000000L是一个最高位是1并且所有低位都是0的二进制数,这样通过按位或的方式可以将(long) bitmapIdx
我们回到PoolArena的allocateNormal方法中:我们分析完了long handle = c.allocate(normCapacity)这步,这里返回的handle就指向chunk中的某个page中的某个子块所对应的连续内存最后,通过iniBuf初始化之后,将创建的chunk加到ChunkList里面这部分在之前的小节我们剖析过,相信大家不会陌生,这里有区别的是if(bitmapIdx == 0)的判断,这里的bitmapIdx不会是0,这样,就会走到initBufWithSubpage(buf, handle, bitmapIdx, reqCapacity)方法中跟到initBufWithSubpage方法:首先拿到memoryMapIdx,这里会将我们之前计算handle传入,跟进去:这里将其强制转化为int类型,也就是去掉高32位,这样就得到memoryMapIdx我们注意在buf调用init方法中的一个参数:runOffset(memoryMapIdx) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize这里的偏移量就是,原来page的偏移量+子块的偏移量bitmapIdx & 0x3FFFFFFF代表当前分配的子page是属于第几个子page(bitmapIdx & 0x3FFFFFFF) * subpage.elemSize表示在当前page的偏移量这样,分配的ByteBuf在内存读写的时候,就会根据偏移量进行读写最后我们跟到init方法中关于“Netty分布式ByteBuf使用subPage级别内存分配的方法”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注百云主机行业资讯频道,小编每天都会为大家更新不同的知识点。

相关推荐: php如何获取时区

这篇文章给大家分享的是有关php如何获取时区的内容。免费云主机域名小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。获得时区如果从代码返回的不是正确的时间,有可能是因为您的服务器位于其他国家或者被设置为不同时区。因此,如果您需要基于具体位置的准…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

Like (0)
Donate 微信扫一扫 微信扫一扫
Previous 07/24 11:32
Next 07/24 11:32

相关推荐