[编织消息框架][netty源码分析]14 PoolChunk 的 PoolSubpage
final class PoolSubpage<T> implements PoolSubpageMetric { //该page分配的chunk final PoolChunk<T> chunk; //内存使用记录 private final long[] bitmap; //该page是否已释放 boolean doNotDestroy; //该page在chunk中的id,通过区段计算偏移 private final int memoryMapIdx; //该page在chunk.memory的偏移量 private final int runOffset; //page大小 private final int pageSize; //page切分后每一段的大小 最少等于16通过 PoolArena#normalizeCapacity方法计算 int elemSize; //page包含的段数量 private int maxNumElems; private int bitmapLength; //下一个可用的位置,用于优化findNextAvail计算 private int nextAvail; //可用的段数量 private int numAvail; //每个分页大小 pageSize = 8192(8k) //分页里每个单元大小 elemSize最少等于16 通过 PoolArena#normalizeCapacity方法计算 void init(PoolSubpage<T> head, int elemSize) { doNotDestroy = true; this.elemSize = elemSize; if (elemSize != 0) { maxNumElems = numAvail = pageSize / elemSize; nextAvail = 0; //所有数据除以64分段 >>>6 等于 /(6*6) bitmapLength = maxNumElems >>> 6; //最后边界加1处理 if ((maxNumElems & 63) != 0) { bitmapLength ++; } //初始化bitmap清零 for (int i = 0; i < bitmapLength; i ++) { bitmap[i] = 0; } } } long allocate() { if (elemSize == 0) { return toHandle(0); } if (numAvail == 0 || !doNotDestroy) { return -1; } final int bitmapIdx = getNextAvail(); //计算出在那一段 int q = bitmapIdx >>> 6; //等于(运算值+1)的倍数,那么结果就为0。如bits & 63 当bits等于64的倍数时结果为0,否则取min(bits,64) 或 bits/64 余数 //这里意思是取当前段的所有状态 int r = bitmapIdx & 63; //设置相应位置状态 bitmap[q] |= 1L << r; //没有可以执行回收 if (-- numAvail == 0) { removeFromPool(); } return toHandle(bitmapIdx); } //这里做了优化,当执行free时会重新计算nextAvail,这时可直接返回 private int getNextAvail() { int nextAvail = this.nextAvail; if (nextAvail >= 0) { this.nextAvail = -1; return nextAvail; } return 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]; //取反不等于0说明还有空位可以使用 if (~bits != 0) { return findNextAvail0(i, bits); } } return -1; } private int findNextAvail0(int i, long bits) { final int maxNumElems = this.maxNumElems; //算出在那一段基础值 final int baseVal = i << 6; //这里用到 >>> 即每位检查是否占用,如果没有占到计算实际id返回 val = baseVal | j for (int j = 0; j < 64; j ++) { // bits & 1 转换成 bits & b01 该第一位二进制没有状态说明该位置没使用 if ((bits & 1) == 0) { //算出当前段第几位 int val = baseVal | j; //少于maxNumElems直接返回,否则返回-1 if (val < maxNumElems) { return val; } else { break; } } //>>>= 右移(向低位移动),左边空出位(高位)以0填充 //>>>1降低一位 bits >>>= 1; } return -1; } private long toHandle(int bitmapIdx) { return 0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx; } //返回true已在使用,返回false已释放 boolean free(PoolSubpage<T> head, int bitmapIdx) { if (elemSize == 0) { return true; } int q = bitmapIdx >>> 6; int r = bitmapIdx & 63; bitmap[q] ^= 1L << r; nextAvail = bitmapIdx; if (numAvail ++ == 0) { addToPool(head); return true; } if (numAvail != maxNumElems) { return true; } else { // Subpage not in use (numAvail == maxNumElems) if (prev == next) { // Do not remove if this subpage is the only one left in the pool. //subpage是pool唯一的,不能删除 return true; } //设置删除记录 doNotDestroy = false; removeFromPool(); return false; } } }
通过PoolSubpage构造时打断点追踪如何分配pageSize,elemSize
PoolArena normalizeCapacity方法分配 elemSize
static int normalizeCapacity(int reqCapacity) { // 大等于512时 双倍增加 if ((reqCapacity & -512) != 0) { // 这里为什么不用 normalizedCapacity<<1 直接加倍,有可能normalizedCapacity刚好是512倍数 int normalizedCapacity = reqCapacity; // 减一是避免双倍自增 normalizedCapacity--; // 将低四位的二进制设置为1,一个int是32位 // 注释掉代码是优化过的,逻辑用for简明示例 // normalizedCapacity |= normalizedCapacity >>> 1; // normalizedCapacity |= normalizedCapacity >>> 2; // normalizedCapacity |= normalizedCapacity >>> 4; // normalizedCapacity |= normalizedCapacity >>> 8; // normalizedCapacity |= normalizedCapacity >>> 16; for (int i = 1; i <= 16; i*=2) { normalizedCapacity |= reqCapacity >>> i; } // 最后要加回1 normalizedCapacity++; // 少于0去掉最高位1即可变正数 if (normalizedCapacity < 0) { normalizedCapacity >>>= 1; } return normalizedCapacity; } // 少于512情况 // 刚好16倍数直接返回 if ((reqCapacity & 15) == 0) { return reqCapacity; } // &~15 相当于 &-16 // 如果少于16结果为0,否则大于16取16的倍数 return (reqCapacity & ~15) + 16; }
我们使用简化的方式来实现PoolSubpage bitmap处理逻辑,方便读者明白如何用long来做储存,应用大量的 & | >>> 等运算
public class TestPoolSubpage { private static int pageSize = 1024*8; private static int maxNumElems = pageSize / 16; private static int bitmapLength = maxNumElems >> 6; static{ if ((maxNumElems & 63) != 0) { bitmapLength ++; } } private static List<Boolean>[] bitmap = new ArrayList[bitmapLength]; public static void main(String[] args) { for (int i = 0; i < bitmapLength; i++) { bitmap[i] = new ArrayList<>(64); } for (int i = 0; i < 65; i++) { findNextAvail(); } } private static void findNextAvail() { for (int i = 0; i < bitmapLength; i++) { if (bitmap[i].size() == 64) { continue; } for (int j = 0; i < 64; j++) { if (bitmap[i].size() <= j || !bitmap[i].get(j)) { if (bitmap[i].size() == j) { bitmap[i].add(true); } else { bitmap[i].set(i, true); } System.out.println("id :" + (i*64 + j) + " section :" + i); break; } } break; } System.out.println(); } }
未完侍。。。
作者: | solq |
博客地址: | http://www.cnblogs.com/solq111 |
博客版权: | 本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。 如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步! 再次感谢您耐心的读完本篇文章。 淘宝店: 海豚极货店 QQ群:9547527 |
如果你热爱生活、热爱编程、热爱吉他。扫一扫加我微信 |
我的新书《编织消息框架》目前进行中,感谢大家关注! |
本作品采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。 |