Netty内存池之PoolSubPage
2018-02-04 15:45
148 查看
在前一篇中刚刚分析了PoolChunk的内存管理策略,简单回顾下PoolChunk的分配原则:
1) 平衡二叉树维护标记内存块的使用情况
2) > 8k, PoolChunk直接在二叉树上标记并分配内存
3) < 8k,在PoolChunk的叶子节点进行相应标记,并交粒度更细的分配工作交给PoolSubPage处理
本文接着PoolChunk中的内存申请<8k的线路分析PoolSubPage的内存分配和释放流程
回顾PoolChunk中分配规则,当申请内存小于8k时,有allocatSubPage分配,首先标记二叉树中可用的叶子节点,找出与与叶子节点subpage在数组中的位置,如果数组为空,创建subpage对象;接着调用subpage的allocat()方法来分配
初始化相应成员之后,调用init(int)方法。
关于bitmap数组说明,PoolSubPage利于bitmap数组来标记内存的使用情况。在Netty内存池管理中,内存最小划分单位为16的字节,那么对于pageSize大小的内存块包含的最小内存块为pageSize/16;当用long类型(64位)的数组bitmap记录内存的使用情况时;PoolSubpage中的bitmap采用long类型的每一位表示一段16字节内存段,因此bitmap数组最大长度为pageSize/16/64即可记录pageSize的内存分配状态。
在init(int)中根据请求内存大小,将PoolChunk叶节点pageSize大小内存块切分位maxNumElems块,而故只需要maxNumElems/64或其+1长度的bitmap即可描述其内存占用状态;然后将PoolsubPage纳入内存池管理
addToPool即将PoolSubpage加入到环形链表中
在subpage初始化完成之后,调用subpage的allocate()
在allocate中,进行参数检查之后,获取bitmap中可以分配的索引段
除第一次查找直接返会0,以后均会调用findNextAvail()搜索可用段
当数组bitmap[i]中数组64位不全为0,即数组索引i中管理内存段可分配,调用findNextAvail(i,bits);
在bitmap可用数组元素中,基准位置段为64*i(数组下标),偏移段为首位=0的位,基准段与偏移段相加即为最终可分配内存段位置
而可分配位置bitmapIdx上,对于bitmap的数组元素下表q =bitmapIdx/64,
该元素中r位(bitmapIdx&63)记录,故将r为标记为不可用1;bitmap[q]|=1L<
至此,PoolSubPage内存管理规则介绍完毕,它与PoolChunk配合分配与管理不同量级的内存分配与释放
1) 平衡二叉树维护标记内存块的使用情况
2) > 8k, PoolChunk直接在二叉树上标记并分配内存
3) < 8k,在PoolChunk的叶子节点进行相应标记,并交粒度更细的分配工作交给PoolSubPage处理
本文接着PoolChunk中的内存申请<8k的线路分析PoolSubPage的内存分配和释放流程
private long allocateSubpage(int normCapacity) { int d = maxOrder; int id = allocateNode(d);//在chunk的叶子节点标记 if (id < 0) { return id; } final PoolSubpage<T>[] subpages = this.subpages; final int pageSize = this.pageSize; freeBytes -= pageSize; int subpageIdx = subpageIdx(id);//找到叶子节点对应的subpage索引 PoolSubpage<T> subpage = subpages[subpageIdx]; if (subpage == null) { subpage = new PoolSubpage<T>(this, id, runOffset(id), pageSize, normCapacity); subpages[subpageIdx] = subpage; } else { subpage.init(normCapacity); } return subpage.allocate(); }
回顾PoolChunk中分配规则,当申请内存小于8k时,有allocatSubPage分配,首先标记二叉树中可用的叶子节点,找出与与叶子节点subpage在数组中的位置,如果数组为空,创建subpage对象;接着调用subpage的allocat()方法来分配
PoolSubpage(PoolChunk<T> chunk, int memoryMapIdx, int runOffset, int pageSize, int elemSize) { this.chunk = chunk; this.memoryMapIdx = memoryMapIdx; //二叉树叶子节点位置 this.runOffset = runOffset; //偏移量 this.pageSize = pageSize; bitmap = new long[pageSize >>> 10]; // pageSize / 16 / 64 init(elemSize); }
初始化相应成员之后,调用init(int)方法。
关于bitmap数组说明,PoolSubPage利于bitmap数组来标记内存的使用情况。在Netty内存池管理中,内存最小划分单位为16的字节,那么对于pageSize大小的内存块包含的最小内存块为pageSize/16;当用long类型(64位)的数组bitmap记录内存的使用情况时;PoolSubpage中的bitmap采用long类型的每一位表示一段16字节内存段,因此bitmap数组最大长度为pageSize/16/64即可记录pageSize的内存分配状态。
void init(int elemSize) { doNotDestroy = true; this.elemSize = elemSize; if (elemSize != 0) { maxNumElems = numAvail = pageSize / elemSize;//按申请内存大小将pageSize划分 nextAvail = 0; bitmapLength = maxNumElems >>> 6; //bitmap中描述内存块的长度 if ((maxNumElems & 63) != 0) { bitmapLength ++; } for (int i = 0; i < bitmapLength; i ++) { bitmap[i] = 0; } } addToPool(); //加入环形链表缓存 }
在init(int)中根据请求内存大小,将PoolChunk叶节点pageSize大小内存块切分位maxNumElems块,而故只需要maxNumElems/64或其+1长度的bitmap即可描述其内存占用状态;然后将PoolsubPage纳入内存池管理
private void addToPool() { PoolSubpage<T> head = chunk.arena.findSubpagePoolHead(elemSize); assert prev == null && next == null; prev = head; next = head.next; next.prev = this; head.next = this; }
addToPool即将PoolSubpage加入到环形链表中
在subpage初始化完成之后,调用subpage的allocate()
long allocate() { if (elemSize == 0) { return toHandle(0); } if (numAvail == 0 || !doNotDestroy) {//无法分配 return -1; } final int bitmapIdx = getNextAvail();//获取可用段 int q = bitmapIdx >>> 6; //bitmap数组中的位置 int r = bitmapIdx & 63; //数组中在long中位的位置 assert (bitmap[q] >>> r & 1) == 0; bitmap[q] |= 1L << r; //将数组中long的对于位标记 if (-- numAvail == 0) { //可用段减1 removeFromPool(); //移除subpage } return toHandle(bitmapIdx); }
在allocate中,进行参数检查之后,获取bitmap中可以分配的索引段
private int getNextAvail() { int nextAvail = this.nextAvail; if (nextAvail >= 0) { this.nextAvail = -1; return nextAvail; } return findNextAvail(); }
除第一次查找直接返会0,以后均会调用findNextAvail()搜索可用段
private int findNextAvail() { final long[] bitmap = this.bitmap; final int bitmapLength = this.bitmapLength; for (int i = 0; i < bitmapLength; i ++) { long bits = bitmap[i]; if (~bits != 0) { //bits位不全为0,可分配 return findNextAvail0(i, bits); } } return -1; }
当数组bitmap[i]中数组64位不全为0,即数组索引i中管理内存段可分配,调用findNextAvail(i,bits);
final int maxNumElems = this.maxNumElems; final int baseVal = i << 6; //基准位置段 for (int j = 0; j < 64; j ++) { //从第1位到64位 if ((bits & 1) == 0) { //第(j+1) 位不为0,可分配 int val = baseVal | j; // 加偏移段 if (val < maxNumElems) { //确定位置 return val; } else { break; } } bits >>>= 1; } return -1; }
在bitmap可用数组元素中,基准位置段为64*i(数组下标),偏移段为首位=0的位,基准段与偏移段相加即为最终可分配内存段位置
而可分配位置bitmapIdx上,对于bitmap的数组元素下表q =bitmapIdx/64,
该元素中r位(bitmapIdx&63)记录,故将r为标记为不可用1;bitmap[q]|=1L<
boolean free(int bitmapIdx) { if (elemSize == 0) { return true; } int q = bitmapIdx >>> 6;//bitmap[q]管理该段内存 int r = bitmapIdx & 63; assert (bitmap[q] >>> r & 1) != 0; //内存已被分配 bitmap[q] ^= 1L << r; //清除分配标记 setNextAvail(bitmapIdx); //bitmapIdx标记为可用 if (numAvail ++ == 0) { //可用段=0 addToPool(); //将PoolSubPage释放至环形链表 return true; } if (numAvail != maxNumElems) { //可用段 没达到最大可以上限 return true; } else { if (prev == next) { //SubPage完全未被分配 return true; } doNotDestroy = false; removeFromPool(); return false; } }
至此,PoolSubPage内存管理规则介绍完毕,它与PoolChunk配合分配与管理不同量级的内存分配与释放
相关文章推荐
- Netty学习之旅------源码分析Netty内存池分配机制初探--PoolArena、PoolChunk、PoolSubpage等数据结构分析
- iOS tabbarviewcontroller 快速点击subviewcontroller崩溃(anonymous namespace)::AutoreleasePoolPage::pop(void
- Netty源码分析:PoolSubpage
- netty5学习笔记-内存池2-PoolSubpage
- netty5学习笔记-内存池2-PoolSubpage
- Redis客户端之Jedis(Key、String、Set、List、pub/sub、cluster、pool、pipleline)
- shared pool 深度解析3(subpool)+
- 如何不丢失innodb的buffer pool的data page?
- A Simple Web Page Template Parser And A Template Pool
- Redis客户端之Jedis(Key、String、Set、List、pub/sub、cluster、pool、pipleline)
- Netty内存池之PoolArea
- Know more about shared pool subpool
- [MOSS]Change your subsite MasterPage
- 水晶报表的统计功能-Crystal Report Sub total Per Page
- Hide a Subpage Using PeopleCode
- Know more about shared pool subpool
- The way to access control in sub-page witin masterpage
- Bug 13250244 - Shared pool leak of "KGLHD" memory when using multiple subpools (文档 ID 13250244.8)
- subpage和secondary page的区别
- Redis客户端之Jedis(Key、String、Set、List、pub/sub、cluster、pool、pipleline)