您的位置:首页 > 其它

Netty内存池之PoolSubPage

2018-02-04 15:45 148 查看
在前一篇中刚刚分析了PoolChunk的内存管理策略,简单回顾下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配合分配与管理不同量级的内存分配与释放
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: