[编织消息框架][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();
}
}
未完侍。。。
[编织消息框架][netty源码分析]14 PoolChunk 的 PoolSubpage的更多相关文章
- [编织消息框架][netty源码分析]1分析切入点
在分析源码之前有几个疑问 1.BOSS线程如何转交给handle(业务)线程2.职业链在那个阶段执行3.socket accept 后转给上层对象是谁4.netty控流算法 另外要了解netty的对象 ...
- [编织消息框架][netty源码分析]2 eventLoop
eventLoop从命名上看是专门处理事件 事件系统主要由线程池同队列技术组成,有以下几个优点 1.任务出队有序执行,不会出现错乱,当然前提执行线程池只有一个 2.解偶系统复杂度,这是个经典的生产者/ ...
- [编织消息框架][netty源码分析]6 ChannelPipeline 实现类DefaultChannelPipeline职责与实现
ChannelPipeline 负责channel数据进出处理,如数据编解码等.采用拦截思想设计,经过A handler处理后接着交给next handler ChannelPipeline 并不是直 ...
- [编织消息框架][netty源码分析]4 eventLoop 实现类NioEventLoop职责与实现
NioEventLoop 是jdk nio多路处理实现同修复jdk nio的bug 1.NioEventLoop继承SingleThreadEventLoop 重用单线程处理 2.NioEventLo ...
- [编织消息框架][netty源码分析]11 UnpooledHeapByteBuf 与 ByteBufAllocator
每种ByteBuf都有相应的分配器ByteBufAllocator,类似工厂模式.我们先学习UnpooledHeapByteBuf与其对应的分配器UnpooledByteBufAllocator 如何 ...
- [编织消息框架][netty源码分析]11 ByteBuf 实现类UnpooledHeapByteBuf职责与实现
每种ByteBuf都有相应的分配器ByteBufAllocator,类似工厂模式.我们先学习UnpooledHeapByteBuf与其对应的分配器UnpooledByteBufAllocator 如何 ...
- [编织消息框架][netty源码分析]5 eventLoop 实现类NioEventLoopGroup职责与实现
分析NioEventLoopGroup最主有两个疑问 1.next work如何分配NioEventLoop 2.boss group 与child group 是如何协作运行的 从EventLoop ...
- [编织消息框架][netty源码分析]8 Channel 实现类NioSocketChannel职责与实现
Unsafe是托委访问socket,那么Channel是直接提供给开发者使用的 Channel 主要有两个实现 NioServerSocketChannel同NioSocketChannel 致于其它 ...
- [编织消息框架][netty源码分析]10 ByteBuf 与 ByteBuffer
因为jdk ByteBuffer使用起来很麻烦,所以netty研发出ByteBuf对象维护管理内存使用ByteBuf有几个概念需要知道1.向ByteBuf提取数据时readerIndex记录最后读取坐 ...
随机推荐
- Linux 链接详解----动态链接库
静态库的缺点: 库函数被包含在每一个运行的进程中,会造成主存的浪费. 目标文件的size过大 每次更新一个模块都需要重新编译,更新困难,使用不方便. 动态库: 是一个目标文件,包含代码和数据,它可以在 ...
- mysql 系统性浅聊 myisam 存储引擎【原创】
>>思维导图 >>介绍 mysql中的存储引擎都是以插件的形式存在,目前用的最多存储引擎就是innodb和myisam.MySQL5.5.5以后(包括5.5.5)默认使用Inn ...
- 51Nod--1012最小公倍数
1012 最小公倍数LCM 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 输入2个正整数A,B,求A与B的最小公倍数. Input 2个数A,B,中间用 ...
- js+画曲线和圆 并限制圆的渲染范围
通过三个点的坐标可确定一条双曲线. 公式: 1)y=ax^2+bx+c; 2) y=a(x-k)+h; 通过已知三点可确定a,b,c,h,k 2.通过圆心坐标(a,b)和半径r可确定一个圆,和已知的x ...
- jqgrid嵌套子表格
jqgrid的subGrid子表格 jqGrid的一项高级功能就是嵌套子表格,使用起来也非常简单.使用的方式有两种: 使用普通的subGrid子表格: 使用一个完整jqGrid作为子表格: 1.选项含 ...
- js 匹配2个字符串相似度
strSimilarity2Number: function (s, t) { var n = s.length, m = t.length, d = []; var i, j, s_i, t_j, ...
- 【liferay】1、使用alloy-UI发送ajax请求
1.首先liferay要发送ajax请求,那么就需要在jsp中定义resourceURL <portlet:resourceURL var="workDeal" id=&qu ...
- WebSocket解析
WebSocket解析 转载请注明出处:WebSocket解析 现在,很多网站为了实现推送技术,所用的技术都是轮询.轮询是指在特定的时间间隔(如每一秒),由浏览器对服务器发起HTTP请求,然后由服务器 ...
- Android之——ListView优化
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/47209253 作为client.其最基本的任务就是最直观的和用户交互.从serve ...
- [Phonegap+Sencha Touch] 移动开发26 Android下的sencha touch程序,转屏时,Ext.Viewport不能触发orientationchange事件的解决的方法
Sencha touch 2.4.2 已经解决问题了. 比方你为Ext.Viewport的orientationchange事件加入了一个监听方法: Ext.Viewport.on('orientat ...