bytebuf池_netty源码解析(4.0)-26 ByteBuf内存池:PoolArena-PoolSubpage
PoolChunk用来分配大于或等于一个page的内存,如果需要小于一个page的内存,需要先从PoolChunk中分配一个page,然后再把一个page切割成多个子页-subpage,最后把内存以subpage为单位分配出去。PoolSubpage就是用来管理subpage的。
一个page会被分割成若干个大小相同的subpage,subpage的的大小是elemSize。elemSize必须是16的整数倍,即必须满足elemSize & 15 == 0。elemSize的取值范围是(16, pageSize-16)。多个elemSize相等的PoolSubpage组成一个双向链表,用于分配特定大小的subpage。
Tiny和Small类型的内存,都是用subpage来分配。Tiny内存大小范围是[16, 512),如果把大小不同的subpage按顺序排列,除最后一个外,任意一个subpage的elemSize+16等于下一个subpage的elemSize,可以用于分配Tiny内存的subpage有512>>4=32种。Small内存大小范围是[512, pageSize),subpage的elemSize=512 * 2n = 2(9+n), 可以用于分配Small内存的subpage有n种,n的最小值是0, 最大值由pageSize决定。
已知:
elemSize < pageSize
pageSize可以表示为2k
elemSize = 29+n
=> 29+n < 2k
=> 9+n < k
=> n < k - 9
=> n的取值范围是(0, k - 9)
上一章中分析过pageShifts, 它就是上面推导过程中使用的变量k。
PoolArena中的PoolSubpage数组
PoolArena维护了两个PoolSubpage表,都是以PoolSubpage[]数组的形式保存:
tinySubpagePools:用于分配Tiny内存,数组长度是521 >> 4 = 32。
smallSubpagePools: 用于分配Small内存,数组长度是pageShifts - 9。
PoolArean在构造方法中初始化这两个数组:
1 tinySubpagePools =newSubpagePoolArray(numTinySubpagePools);2 for (int i = 0; i < tinySubpagePools.length; i ++) {3 tinySubpagePools[i] =newSubpagePoolHead(pageSize);4 }5
6 numSmallSubpagePools = pageShifts - 9;7 smallSubpagePools =newSubpagePoolArray(numSmallSubpagePools);8 for (int i = 0; i < smallSubpagePools.length; i ++) {9 smallSubpagePools[i] =newSubpagePoolHead(pageSize);10 }
代码中的numTinySubpagePools=512>>4和numSmallSubpagePools=pageShifts - 9,分别是两个数组的长度。这两个数组保存的都是PoolSubpage双向链表的头节点,头节点不能用来分配内存。
findSubpagePoolHead方法可以根据elemSize找到对应的PoolSubpage链表的头节点:
1 PoolSubpage findSubpagePoolHead(intelemSize) {2 inttableIdx;3 PoolSubpage[] table;4 if (isTiny(elemSize)) { //< 512
5 tableIdx = elemSize >>> 4;6 table =tinySubpagePools;7 } else{8 tableIdx = 0;9 elemSize >>>= 10;10 while (elemSize != 0) {11 elemSize >>>= 1;12 tableIdx ++;13 }14 table =smallSubpagePools;15 }16
17 returntable[tableIdx];18 }
4-6行,如果是Tiny内存,计算elemSize在tinySubpagePools中的偏移量tableIdx。
8-14行,如果是Normal内存,计算elemSize在smallSubpagePools中的偏移量tabIeIdx。计算tableIdx的算法是把elemSize无符号右移10位之后,找非0的最高位,在找的过程中累加tableIdx,找到之后及得到了正确的偏移量。这个算法还可以简化成log2(elemSize) - 9。
17行,取出一个PoolSubpage链表头。
PoolSubpage初始化
在PoolChunk的allocateSubpage方法中,调用findSubpagePoolHead得到一个head,然后使用分配到的二叉树内存节点初始化一个PoolSubpage节点。一个能用来分配内存的PooSubpage节点可以调用构造方法或init方法进行初始化。
1 PoolSubpage(PoolSubpage head, PoolChunk chunk, int memoryMapIdx, int runOffset, int pageSize, intelemSize) {2 this.chunk =chunk;3 this.memoryMapIdx =memoryMapIdx;4 this.runOffset =runOffset;5 this.pageSize =pageSize;6 bitmap = new long[pageSize >>> 10]; //pageSize / 16 / 64
7 init(head, elemSize);8 }
这个构造方法只是做了一些简单的属性初始化工作。第6行初始bitmap,它用bit位来记录每个subpage的使用情况,每个bit对应一个subpage,0表示subpage空闲,1表示subpage已经被分配出去。一个subpage的大小是elemSize,前面已经讲过,最小的elemSize=16, 那么一个page最多可分割成subpage的数量maxSubpageCount=pageSize/16=pageSize >> 4。bitmap是个long型的数字,每个long数据有64位,因此bitmap的最大长度只需要maxBitmapLength = maxSubpageCount / 64 = pageSize / 16 / 64 = pageSize >> 10就够用了。
init方法的作用是根据elemSize计算出有效的bitmap长度bitmapLength,然后把bitmapLength范围内存的bit值都初始化为0。
1 void init(PoolSubpage head, intelemSize) {2 doNotDestroy = true;3 this.elemSize =elemSize;4 if (elemSize != 0) {5 maxNumElems = numAvail = pageSize /elemSize;6 nextAvail = 0;7 bitmapLength = maxNumElems >>> 6;8 if ((maxNumElems & 63) != 0) {9 bitmapLength ++;10 }11
12 for (int i = 0; i < bitmapLength; i ++) {13 bitmap[i] = 0;14 }15 }16 addToPool(head);17 }
第5行,初始subpage的最大数量maxNumElems和可用数量numAvail。
第6行,初始化下一个可用subpage的索引。
第7行, 计算bitmap的有效数量,bitmapLength = maxNumElems >>> 6 = maxNumElems / 64。
第8,9行,如果maxNumElems不是64的整数倍,bitmapLength需要额外加1。
第12-14行,有效长度的bitmap值都设置成0。
第16行, 把当前PoolSubpage节点添加到head后面。
bitmap位的索引范围是[0, maxNumElems)。
分配一个subpage
PoolSubpage初始化完成之后,调用allocate可以分配一个subpage,返回的是一个long型的的handle,这个handle代表一块内存。handle的低32位memoryMapIdx,是PoolChunk中二叉树节点的索引;高32位bitmapIdx,是subpage在bitmap中对应位的索引。
分配一个subpage有两个步骤:
找到一个可用subpage的索引bitmapIdx
把这个bitmapIdx在bitmap中对应的bit为置为1
findNextAvail方法负责到底一个可用的subpage并返回它的bitmapIdx。
1 private intfindNextAvail() {2 final long[] bitmap = this.bitmap;3 final int bitmapLength = this.bitmapLength;4 for (int i = 0; i < bitmapLength; i ++) {5 long bits =bitmap[i];6 if (~bits != 0) {7 returnfindNextAvail0(i, bits);8 }9 }10 return -1;11 }12
13 private int findNextAvail0(int i, longbits) {14 final int maxNumElems = this.maxNumElems;15 final int baseVal = i << 6;16
17 for (int j = 0; j < 64; j ++) {18 if ((bits & 1) == 0) {19 int val = baseVal |j;20 if (val >>= 1;27 }28 return -1;29 }
第1-11行,变量bitmap数组,找到一个至少有一位是0的long数据。~bits != 0 说明bits中至少有一位是0。然后调用findNextAvail0找到bits中为0的最低位。
第15行,计算bitmap数组中的索引i对应的bit索引baseVal = i << 6 = i * 64。
第17-27行,遍历bits的每个bit位,遇到为0的bit后在19计算回bitmpaIdx = baseVal | j,j表示bit位在long数据中bit索引。如果满足bitmapIdx < maxNumElems在21返回。
第28行,如果没找到可用的subpage, 返回-1。当maxNumElems不是64的整数倍时,bitmap数组中最后一个bits在~bits != 0的情况下可能已经没有subpage可用。
allcate方法是分配supage的入口,它调用getNextAvail得到一个supage的bitmapIdx, getNextAvail在nextAvail属性为-1的时候,调用findNexAvail。然后把bitmapIdx对应的bit为置为1,最后返回handle。
1 longallocate() {2 if (elemSize == 0) {3 return toHandle(0);4 }5
6 if (numAvail == 0 || !doNotDestroy) {7 return -1;8 }9
10 final int bitmapIdx =getNextAvail();11 int q = bitmapIdx >>> 6;12 int r = bitmapIdx & 63;13 assert (bitmap[q] >>> r & 1) == 0;14 bitmap[q] |= 1L <
16 if (-- numAvail == 0) {17 removeFromPool();18 }19
20 returntoHandle(bitmapIdx);21 }
第10行,得到下一个可用的subpage在bitmap中的索引bitmapIdx。
第11行,计算bitmapIdx在bitpmap数组中索引,q = bitmapIdx >>> 6 = (int)(bitmapIdx/64)。
第12行,计算bitmapIdx对应的bit在long数据中的位索引r,表示q对应的long数据的第r位就是。
第14行,把bitmapIdx对应的bit为设置为1。
第16,17行,把可用subpage数numAvail减1,如果numAvail==0表示当前PoolSubpage节点已经没有可用的subpage了,调用removeFromPool把它从链表中删除。
第20行,把bitmapIdx转换成表示内存的handle,算法是: handle = 0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;
释放一个subpage
free方法实现了subpage释放的功能,和allocate相比要简单的多,它的主要工作是把bitmapIdx对应的bit为设置为0,还顺便做了一下清理善后工作。
1 boolean free(PoolSubpage head, intbitmapIdx) {2 if (elemSize == 0) {3 return true;4 }5 int q = bitmapIdx >>> 6;6 int r = bitmapIdx & 63;7 assert (bitmap[q] >>> r & 1) != 0;8 bitmap[q] ^= 1L <
10 setNextAvail(bitmapIdx);11
12 if (numAvail ++ == 0) {13 addToPool(head);14 return true;15 }16
17 if (numAvail !=maxNumElems) {18 return true;19 } else{20 //Subpage not in use (numAvail == maxNumElems)
21 if (prev ==next) {22 //Do not remove if this subpage is the only one left in the pool.
23 return true;24 }25
26 //Remove this subpage from the pool if there are other subpages left in the pool.
27 doNotDestroy = false;28 removeFromPool();29 return false;30 }31 }
第5,6行,和allocate中解释过。
第8行,把bitmapIdx对应的bit为置为0。
第10行,把这个bitmapIdx赋值给nextAvail属性,这样在一次或多次调用free之后的第一次allocate调用就不会调用findNextAvail方法,可以提升allocate的性能。
第12,13行,当前PoolSubpage节点中至少有一个可用的subpage,把当前节点添加到链表中。
第27-29行,当前PooSubpage节点中所有分配出去的节点都全部还会来了,换言之,当前节点有回到bitmap初始化状态,把当前节点从链表中删除。
bytebuf池_netty源码解析(4.0)-26 ByteBuf内存池:PoolArena-PoolSubpage相关推荐
- 【Android 异步操作】线程池 ( 线程池作用 | 线程池种类 | 线程池工作机制 | 线程池任务调度源码解析 )
文章目录 一.线程池作用 二.线程池种类 三.线程池工作机制 四.线程池任务调度源码解析 一.线程池作用 线程池作用 : ① 避免创建线程 : 避免每次使用线程时 , 都需要 创建线程对象 ; ② 统 ...
- Apache IoTDB源码解析(0.11.2版本):Session的源码解析
1. 声明 当前内容主要为解析Apache IoTDB 0.11.2版本的Session的源码解析 通过前面的Apache Thrift的Demo,可以发现iotdb中的server是使用了thrif ...
- ceph bluestore源码分析:admin_socket实时获取内存池数据
环境: 版本:ceph 12.2.1 部署完cephfs 使用ceph-fuse挂载,并写入数据 关键参数: debug_mempool = true 将该参数置为true即可查看详细的blustor ...
- netty获取玩家chanel_netty源码解析(4.0)-14 Channel NIO实现:读取数据
本章分析Nio Channel的数据读取功能的实现. Channel读取数据需要Channel和ChannelHandler配合使用,netty设计数据读取功能包括三个要素:Channel, Even ...
- 【Android 异步操作】线程池 ( 线程池 execute 方法源码解析 )
文章目录 一.线程池 execute 方法源码解析 二.线程池 execute 方法完整源码及注释 一.线程池 execute 方法源码解析 进入 ThreadPoolExecutor 中 , 查看线 ...
- 数值分析 使用c语言 源码_分析源码,学会正确使用 Java 线程池
在日常的开发工作当中,线程池往往承载着一个应用中最重要的业务逻辑,因此我们有必要更多地去关注线程池的执行情况,包括异常的处理和分析等.本文主要聚焦在如何正确使用线程池上,以及提供一些实用的建议.文中会 ...
- balanced-match 源码解析
balanced-match 源码解析 文章目录 balanced-match 源码解析 正文 0. 基本信息 0.1 Usage 0.2 Version: v2.0.0 0.2 Doc 1. 源码解 ...
- inflight 源码解析
inflight 源码解析 文章目录 inflight 源码解析 正文 0. 基本信息 1. 源码解析 1.0 源码结构 1.1 主入口 inflight 1.2 启动函数创建着 makeres 其他 ...
- netty4.0源码解析(持续更新)
文章目录 netty 1:环境配置 与其他对比 future缺陷 2:结构 3:设计模式 delay 策略模式 todo 责任链模式 todo 单例模式模式 todo 装饰者模式 ReplayingD ...
最新文章
- 试试这个文字冒险游戏,故事是AI写的:情节丰满逻辑不乱,进去就出不来了,在线可玩...
- 全字库说文解字字体_【180期】可商用字体大全,无版权纠纷!
- iOS从生成证书到打包上架-02(详细2016-10最新)
- 认识CSS中高级技巧之元素的显示与隐藏
- python如何导入类里_Python导入模块中的所有类(98)
- Tomcat部署到CentOS7
- 基于python的多光谱影像植被指数计算
- 中控考勤仪IFace302多线程操作时无法订阅事件
- 高帝制礼作乐,周昌谏废太子
- 【业务安全06】接口参数账号修改漏洞——基于metinfov4.0平台
- java实现发送国际短信的功能
- 项目推送到远程gitLab库时一般需要排除什么文件及其如何排除?
- 计算机发展史上代表性的人物,计算机发展史最具影响力人物
- 习题6-8 单词首字母大写 (15 分)
- Cocos2d-x 实现地图滚动,解释缝隙产生的原因以及解决方案
- Sentiment Word Aware Multimodal Refinement for MultimodalSentiment Analysis with ASR Errors
- x是偶数的c语言表达式,【单选题】能表示x 为偶数的表达式是
A. x%2==0
B. x%2==1
C. x-. x%2!=0...
- ZigBee——在CC2530的ZStack中添加定时任务
- 量化交易策略 alpha策略
- Anaconda python3.6版本
热门文章
- Linux错误27,解决在linux下编译32程序出现“/usr/include/gnu/stubs.h:7:27: 致命错误:gnu/stubs-32.h:没有那个文件或目录问题”...
- linux .o文件,Linux 文件I/O
- php不显示内容里的图片不显示,图片显示不出来,但是数据库里有显示
- java 代码块的意义_Java基础(9) - 静态、代码块
- php函数trim(),php trim函数怎么用?
- 怎么潜入别人家_小学生发明防雾口罩,别人家的孩子是怎么养成的?
- mysql的to datetime_mysql-笔记-datetime
- bt云服务器地址,windows2008搭建bttracker服务器
- linux 硬盘空间还有,但是无法创建文件
- 玩转 SpringBoot 2 之整合定时任务篇