您好,登录后才能下订单哦!
这篇文章主要介绍“Netty源码解析之如何理解内存对齐类SizeClasses”,在日常操作中,相信很多人在Netty源码解析之如何理解内存对齐类SizeClasses问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Netty源码解析之如何理解内存对齐类SizeClasses”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
在学习Netty内存池之前,我们先了解一下Netty的内存对齐类SizeClasses,它为Netty内存池中的内存块提供大小对齐,索引计算等服务方法。
源码分析基于Netty 4.1.52
Netty内存池中每个内存块size都符合如下计算公式
size = 1 << log2Group + nDelta * (1 << log2Delta)
log2Group:内存块分组
nDelta:增量乘数
log2Delta:增量大小的log2值
SizeClasses初始化后,将计算chunkSize(内存池每次向操作系统申请内存块大小)范围内每个size的值,保存到sizeClasses字段中。
sizeClasses是一个表格(二维数组),共有7列,含义如下
index:内存块size的索引
log2Group:内存块分组,用于计算对应的size
log2Delata:增量大小的log2值,用于计算对应的size
nDelta:增量乘数,用于计算对应的size
isMultipageSize:表示size是否为page的倍数
isSubPage:表示是否为一个subPage类型
log2DeltaLookup:如果size存在位图中的,记录其log2Delta,未使用
sizeClasses负责计算sizeClasses表格
private int sizeClasses() { int normalMaxSize = -1; int index = 0; int size = 0; // #1 int log2Group = LOG2_QUANTUM; int log2Delta = LOG2_QUANTUM; int ndeltaLimit = 1 << LOG2_SIZE_CLASS_GROUP; // #2 int nDelta = 0; while (nDelta < ndeltaLimit) { size = sizeClass(index++, log2Group, log2Delta, nDelta++); } log2Group += LOG2_SIZE_CLASS_GROUP; // #3 while (size < chunkSize) { nDelta = 1; while (nDelta <= ndeltaLimit && size < chunkSize) { size = sizeClass(index++, log2Group, log2Delta, nDelta++); normalMaxSize = size; } log2Group++; log2Delta++; } //chunkSize must be normalMaxSize assert chunkSize == normalMaxSize; //return number of size index return index; }
LOG2_QUANTUM=4
LOG2_SIZE_CLASS_GROUP=2
#1
log2Group,log2Delta都是从LOG2_QUANTUM开始
ndeltaLimit为2^LOG2_SIZE_CLASS_GROUP,即内存块size以4个为一组进行分组
#2
初始化第0组
nDelta从0开始
sizeClass方法计算sizeClasses每一行内容
注意:第0组后log2Group增加LOG2_SIZE_CLASS_GROUP,而log2Delta不变
#3
初始化后面的size
nDelta从1开始
每组log2Group+1,log2Delta+1
将log2Group=log2Delta+LOG2_SIZE_CLASS_GROUP
代入计算公式中,得到
size = 1 << (log2Delta+LOG2_SIZE_CLASS_GROUP) + nDelta * (1 << log2Delta)
size = (nDelta + 2 ^ LOG2_SIZE_CLASS_GROUP) * (1 << log2Delta)
可以看到,每个内存块size都是(1 << log2Delta)的倍数
从第二组开始,每组内这个倍数依次是5,6,7,8
每组内相邻行大小增量为(1 << log2Delta),相邻组之间(1 << log2Delta)翻倍。
Netty默认的配置一个page的大小是2^13,即为8KB,默认的一个chunk的大小为16777216,即16MB。sizeClasses表格内存如下:
Netty内存池中管理了大小不同的内存块,对于这些不同大小的内存块,Netty划分为不同的等级Small,Normal,Huge。Huge是大于chunkSize的内存块,不在表格中,这里也不讨论。
sizeClasses表格可以分为两部分
isSubPage为1的size为Small内存块,其他为Normal内存块。 分配Small内存块,需要找到对应的index 通过size2SizeIdx方法计算index 比如需要分配一个90字节的内存块,需要从sizeClasses表格找到第一个大于90的内存块size,即96,其index为5。
Normal内存块必须是page的倍数。 将isMultipageSize为1的行取出组成另一个表格
PoolChunk中分配Normal内存块需求查询对应的pageIdx。
比如要分配一个50000字节的内存块,需要从这个新表格找到第一个大于50000的内存块size,即57344,其pageIdx为6。
通过pages2pageIdxCompute方法计算pageIdx。
下面看一下具体的计算方法
public int size2SizeIdx(int size) { if (size == 0) { return 0; } // #1 if (size > chunkSize) { return nSizes; } // #2 if (directMemoryCacheAlignment > 0) { size = alignSize(size); } // #3 if (size <= lookupMaxSize) { //size-1 / MIN_TINY return size2idxTab[size - 1 >> LOG2_QUANTUM]; } // #4 int x = log2((size << 1) - 1); // #5 int shift = x < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM + 1 ? 0 : x - (LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM); int group = shift << LOG2_SIZE_CLASS_GROUP; // #6 int log2Delta = x < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM + 1 ? LOG2_QUANTUM : x - LOG2_SIZE_CLASS_GROUP - 1; // #7 int deltaInverseMask = -1 << log2Delta; int mod = (size - 1 & deltaInverseMask) >> log2Delta & (1 << LOG2_SIZE_CLASS_GROUP) - 1; return group + mod; }
#1
大于chunkSize,就是返回nSizes代表申请的是Huge内存块。
#2
不使用sizeClasses表格,直接将申请内存大小转换为directMemoryCacheAlignment的倍数,directMemoryCacheAlignment默认为0。
#3
SizeClasses将一部分较小的size与对应index记录在size2idxTab作为位图,这里直接查询size2idxTab,避免重复计算
size2idxTab中保存了(size-1)/(2^LOG2_QUANTUM) --> idx的对应关系。
从sizeClasses方法可以看到,sizeClasses表格中每个size都是(2^LOG2_QUANTUM) 的倍数。
#4
对申请内存大小进行log2的向上取整,就是每组最后一个内存块size。-1是为了避免申请内存大小刚好等于2的指数次幂时被翻倍。
将log2Group = log2Delta + LOG2_SIZE_CLASS_GROUP
,nDelta=2^LOG2_SIZE_CLASS_GROUP
代入计算公式,可得
lastSize = 1 << (log2Group + 1)
即x = log2Group + 1
#5
shift, 当前在第几组,从0开始(sizeClasses表格中0~3
行为第0组,4~7
行为第1组,以此类推,不是log2Group)
x < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM + 1
,即log2Group < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM
,满足该条件的是第0组的size,这时shift固定是0。
从sizeClasses方法可以看到,除了第0组,都满足shift = log2Group - LOG2_QUANTUM - (LOG2_SIZE_CLASS_GROUP - 1)
。
shift << LOG2_SIZE_CLASS_GROUP
就是该组第一个内存块size的索引
#6
计算log2Delta
第0组固定是LOG2_QUANTUM
除了第0组,将nDelta = 2^LOG2_SIZE_CLASS_GROUP
代入计算公式
lastSize = ( 2^LOG2_SIZE_CLASS_GROUP + 2^LOG2_SIZE_CLASS_GROUP ) * (1 << log2Delta)
lastSize = (1 << log2Delta) << LOG2_SIZE_CLASS_GROUP << 1
#7
前面已经定位到第几组了,下面要找到申请内存大小应分配在该组第几位
这里要找到比申请内存大的最小size。
申请内存大小可以理解为上一个size加上一个不大于(1 << log2Delta)的值,即
(nDelta - 1 + 2^LOG2_SIZE_CLASS_GROUP) * (1 << log2Delta) + n
, 备注:0 < n <= (1 << log2Delta)
注意,nDelta - 1就是mod
& deltaInverseMask
,将申请内存大小最后log2Delta个bit位设置为0,可以理解为减去n
>> log2Delta
,右移log2Delta个bit位,就是除以(1 << log2Delta),结果就是(nDelta - 1 + 2 ^ LOG2_SIZE_CLASS_GROUP)
& (1 << LOG2_SIZE_CLASS_GROUP) - 1
, 取最后的LOG2_SIZE_CLASS_GROUP个bit位的值,结果就是mod
size - 1
,是为了申请内存等于内存块size时避免分配到下一个内存块size中,即n == (1 << log2Delta)的场景。
疑问:既然右移log2Delta个bit位,那为什么前面要将log2Delta个bit位设置为0?
第0组由于log2Group等于log2Delta,代入计算公式如下
1 << log2Delta + (nDelta - 1) * (1 << log2Delta) + n
, 备注:0 < n <= (1 << log2Delta)
nDelta * (1 << log2Delta) + n
所以第0组nDelta从0开始,mod = nDelta
pages2pageIdxCompute方法计算pageIdx逻辑与size2SizeIdx方法类似,只是将LOG2_QUANTUM变量换成了pageShifts,这里不再重复。
SizeClasses是给PoolArena(内存池),PoolChunk(内存块)提供服务的,建议大家结合后面分析PoolArena,PoolChunk的文章一起理解。
如果大家对SizeClasses具体算法不感兴趣,只有理解SizeClasses类中利用sizeClasses表格,为PoolArena,PoolChunk提供计算index,pageIdx索引的方法,也可以帮助大家理解后面解析PoolArena,PoolChunk的文章。
下面贴出sizeClasses完整表格(可复制到Excle,以|分列)
| index | log2Group | log2Delta | nDelta | isMultiPageSize | isSubPage | log2DeltaLookup | size | usize | | 0 | 4 | 4 | 0 | 0 | 1 | 4 | 16 | | | 1 | 4 | 4 | 1 | 0 | 1 | 4 | 32 | | | 2 | 4 | 4 | 2 | 0 | 1 | 4 | 48 | | | 3 | 4 | 4 | 3 | 0 | 1 | 4 | 64 | | | 4 | 6 | 4 | 1 | 0 | 1 | 4 | 80 | | | 5 | 6 | 4 | 2 | 0 | 1 | 4 | 96 | | | 6 | 6 | 4 | 3 | 0 | 1 | 4 | 112 | | | 7 | 6 | 4 | 4 | 0 | 1 | 4 | 128 | | | 8 | 7 | 5 | 1 | 0 | 1 | 5 | 160 | | | 9 | 7 | 5 | 2 | 0 | 1 | 5 | 192 | | | 10 | 7 | 5 | 3 | 0 | 1 | 5 | 224 | | | 11 | 7 | 5 | 4 | 0 | 1 | 5 | 256 | | | 12 | 8 | 6 | 1 | 0 | 1 | 6 | 320 | | | 13 | 8 | 6 | 2 | 0 | 1 | 6 | 384 | | | 14 | 8 | 6 | 3 | 0 | 1 | 6 | 448 | | | 15 | 8 | 6 | 4 | 0 | 1 | 6 | 512 | | | 16 | 9 | 7 | 1 | 0 | 1 | 7 | 640 | | | 17 | 9 | 7 | 2 | 0 | 1 | 7 | 768 | | | 18 | 9 | 7 | 3 | 0 | 1 | 7 | 896 | | | 19 | 9 | 7 | 4 | 0 | 1 | 7 | 1024 | 1K | | 20 | 10 | 8 | 1 | 0 | 1 | 8 | 1280 | 1.25K | | 21 | 10 | 8 | 2 | 0 | 1 | 8 | 1536 | 1.5K | | 22 | 10 | 8 | 3 | 0 | 1 | 8 | 1792 | 1.75K | | 23 | 10 | 8 | 4 | 0 | 1 | 8 | 2048 | 2K | | 24 | 11 | 9 | 1 | 0 | 1 | 9 | 2560 | 2.5K | | 25 | 11 | 9 | 2 | 0 | 1 | 9 | 3072 | 3K | | 26 | 11 | 9 | 3 | 0 | 1 | 9 | 3584 | 3.5K | | 27 | 11 | 9 | 4 | 0 | 1 | 9 | 4096 | 4K | | 28 | 12 | 10 | 1 | 0 | 1 | 0 | 5120 | 5K | | 29 | 12 | 10 | 2 | 0 | 1 | 0 | 6144 | 6K | | 30 | 12 | 10 | 3 | 0 | 1 | 0 | 7168 | 7K | | 31 | 12 | 10 | 4 | 1 | 1 | 0 | 8192 | 8K | | 32 | 13 | 11 | 1 | 0 | 1 | 0 | 10240 | 10K | | 33 | 13 | 11 | 2 | 0 | 1 | 0 | 12288 | 12K | | 34 | 13 | 11 | 3 | 0 | 1 | 0 | 14336 | 14K | | 35 | 13 | 11 | 4 | 1 | 1 | 0 | 16384 | 16K | | 36 | 14 | 12 | 1 | 0 | 1 | 0 | 20480 | 20K | | 37 | 14 | 12 | 2 | 1 | 1 | 0 | 24576 | 24K | | 38 | 14 | 12 | 3 | 0 | 1 | 0 | 28672 | 28K | | 39 | 14 | 12 | 4 | 1 | 0 | 0 | 32768 | 32K | | 40 | 15 | 13 | 1 | 1 | 0 | 0 | 40960 | 40K | | 41 | 15 | 13 | 2 | 1 | 0 | 0 | 49152 | 48K | | 42 | 15 | 13 | 3 | 1 | 0 | 0 | 57344 | 56K | | 43 | 15 | 13 | 4 | 1 | 0 | 0 | 65536 | 64K | | 44 | 16 | 14 | 1 | 1 | 0 | 0 | 81920 | 80K | | 45 | 16 | 14 | 2 | 1 | 0 | 0 | 98304 | 96K | | 46 | 16 | 14 | 3 | 1 | 0 | 0 | 114688 | 112K | | 47 | 16 | 14 | 4 | 1 | 0 | 0 | 131072 | 128K | | 48 | 17 | 15 | 1 | 1 | 0 | 0 | 163840 | 160K | | 49 | 17 | 15 | 2 | 1 | 0 | 0 | 196608 | 192K | | 50 | 17 | 15 | 3 | 1 | 0 | 0 | 229376 | 224K | | 51 | 17 | 15 | 4 | 1 | 0 | 0 | 262144 | 256K | | 52 | 18 | 16 | 1 | 1 | 0 | 0 | 327680 | 320K | | 53 | 18 | 16 | 2 | 1 | 0 | 0 | 393216 | 384K | | 54 | 18 | 16 | 3 | 1 | 0 | 0 | 458752 | 448K | | 55 | 18 | 16 | 4 | 1 | 0 | 0 | 524288 | 512K | | 56 | 19 | 17 | 1 | 1 | 0 | 0 | 655360 | 640K | | 57 | 19 | 17 | 2 | 1 | 0 | 0 | 786432 | 768K | | 58 | 19 | 17 | 3 | 1 | 0 | 0 | 917504 | 896K | | 59 | 19 | 17 | 4 | 1 | 0 | 0 | 1048576 | 1M | | 60 | 20 | 18 | 1 | 1 | 0 | 0 | 1310720 | 1.25M | | 61 | 20 | 18 | 2 | 1 | 0 | 0 | 1572864 | 1.5M | | 62 | 20 | 18 | 3 | 1 | 0 | 0 | 1835008 | 1.75M | | 63 | 20 | 18 | 4 | 1 | 0 | 0 | 2097152 | 2M | | 64 | 21 | 19 | 1 | 1 | 0 | 0 | 2621440 | 2.5M | | 65 | 21 | 19 | 2 | 1 | 0 | 0 | 3145728 | 3M | | 66 | 21 | 19 | 3 | 1 | 0 | 0 | 3670016 | 3.5M | | 67 | 21 | 19 | 4 | 1 | 0 | 0 | 4194304 | 4M | | 68 | 22 | 20 | 1 | 1 | 0 | 0 | 5242880 | 5M | | 69 | 22 | 20 | 2 | 1 | 0 | 0 | 6291456 | 6M | | 70 | 22 | 20 | 3 | 1 | 0 | 0 | 7340032 | 7M | | 71 | 22 | 20 | 4 | 1 | 0 | 0 | 8388608 | 8M | | 72 | 23 | 21 | 1 | 1 | 0 | 0 | 10485760 | 10M | | 73 | 23 | 21 | 2 | 1 | 0 | 0 | 12582912 | 12M | | 74 | 23 | 21 | 3 | 1 | 0 | 0 | 14680064 | 14M | | 75 | 23 | 21 | 4 | 1 | 0 | 0 | 16777216 | 16M |
到此,关于“Netty源码解析之如何理解内存对齐类SizeClasses”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。