您好,登錄后才能下訂單哦!
這篇文章主要介紹“Netty源碼解析之如何理解內(nèi)存對(duì)齊類SizeClasses”,在日常操作中,相信很多人在Netty源碼解析之如何理解內(nèi)存對(duì)齊類SizeClasses問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Netty源碼解析之如何理解內(nèi)存對(duì)齊類SizeClasses”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
在學(xué)習(xí)Netty內(nèi)存池之前,我們先了解一下Netty的內(nèi)存對(duì)齊類SizeClasses,它為Netty內(nèi)存池中的內(nèi)存塊提供大小對(duì)齊,索引計(jì)算等服務(wù)方法。
源碼分析基于Netty 4.1.52
Netty內(nèi)存池中每個(gè)內(nèi)存塊size都符合如下計(jì)算公式
size = 1 << log2Group + nDelta * (1 << log2Delta)
log2Group:內(nèi)存塊分組
nDelta:增量乘數(shù)
log2Delta:增量大小的log2值
SizeClasses初始化后,將計(jì)算chunkSize(內(nèi)存池每次向操作系統(tǒng)申請(qǐng)內(nèi)存塊大?。┓秶鷥?nèi)每個(gè)size的值,保存到sizeClasses字段中。
sizeClasses是一個(gè)表格(二維數(shù)組),共有7列,含義如下
index:內(nèi)存塊size的索引
log2Group:內(nèi)存塊分組,用于計(jì)算對(duì)應(yīng)的size
log2Delata:增量大小的log2值,用于計(jì)算對(duì)應(yīng)的size
nDelta:增量乘數(shù),用于計(jì)算對(duì)應(yīng)的size
isMultipageSize:表示size是否為page的倍數(shù)
isSubPage:表示是否為一個(gè)subPage類型
log2DeltaLookup:如果size存在位圖中的,記錄其log2Delta,未使用
sizeClasses負(fù)責(zé)計(jì)算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開(kāi)始
ndeltaLimit為2^LOG2_SIZE_CLASS_GROUP,即內(nèi)存塊size以4個(gè)為一組進(jìn)行分組
#2
初始化第0組
nDelta從0開(kāi)始
sizeClass方法計(jì)算sizeClasses每一行內(nèi)容
注意:第0組后log2Group增加LOG2_SIZE_CLASS_GROUP,而log2Delta不變
#3
初始化后面的size
nDelta從1開(kāi)始
每組log2Group+1,log2Delta+1
將log2Group=log2Delta+LOG2_SIZE_CLASS_GROUP
代入計(jì)算公式中,得到
size = 1 << (log2Delta+LOG2_SIZE_CLASS_GROUP) + nDelta * (1 << log2Delta)
size = (nDelta + 2 ^ LOG2_SIZE_CLASS_GROUP) * (1 << log2Delta)
可以看到,每個(gè)內(nèi)存塊size都是(1 << log2Delta)的倍數(shù)
從第二組開(kāi)始,每組內(nèi)這個(gè)倍數(shù)依次是5,6,7,8
每組內(nèi)相鄰行大小增量為(1 << log2Delta),相鄰組之間(1 << log2Delta)翻倍。
Netty默認(rèn)的配置一個(gè)page的大小是2^13,即為8KB,默認(rèn)的一個(gè)chunk的大小為16777216,即16MB。sizeClasses表格內(nèi)存如下:
Netty內(nèi)存池中管理了大小不同的內(nèi)存塊,對(duì)于這些不同大小的內(nèi)存塊,Netty劃分為不同的等級(jí)Small,Normal,Huge。Huge是大于chunkSize的內(nèi)存塊,不在表格中,這里也不討論。
sizeClasses表格可以分為兩部分
isSubPage為1的size為Small內(nèi)存塊,其他為Normal內(nèi)存塊。 分配Small內(nèi)存塊,需要找到對(duì)應(yīng)的index 通過(guò)size2SizeIdx方法計(jì)算index 比如需要分配一個(gè)90字節(jié)的內(nèi)存塊,需要從sizeClasses表格找到第一個(gè)大于90的內(nèi)存塊size,即96,其index為5。
Normal內(nèi)存塊必須是page的倍數(shù)。 將isMultipageSize為1的行取出組成另一個(gè)表格
PoolChunk中分配Normal內(nèi)存塊需求查詢對(duì)應(yīng)的pageIdx。
比如要分配一個(gè)50000字節(jié)的內(nèi)存塊,需要從這個(gè)新表格找到第一個(gè)大于50000的內(nèi)存塊size,即57344,其pageIdx為6。
通過(guò)pages2pageIdxCompute方法計(jì)算pageIdx。
下面看一下具體的計(jì)算方法
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代表申請(qǐng)的是Huge內(nèi)存塊。
#2
不使用sizeClasses表格,直接將申請(qǐng)內(nèi)存大小轉(zhuǎn)換為directMemoryCacheAlignment的倍數(shù),directMemoryCacheAlignment默認(rèn)為0。
#3
SizeClasses將一部分較小的size與對(duì)應(yīng)index記錄在size2idxTab作為位圖,這里直接查詢size2idxTab,避免重復(fù)計(jì)算
size2idxTab中保存了(size-1)/(2^LOG2_QUANTUM) --> idx的對(duì)應(yīng)關(guān)系。
從sizeClasses方法可以看到,sizeClasses表格中每個(gè)size都是(2^LOG2_QUANTUM) 的倍數(shù)。
#4
對(duì)申請(qǐng)內(nèi)存大小進(jìn)行l(wèi)og2的向上取整,就是每組最后一個(gè)內(nèi)存塊size。-1是為了避免申請(qǐng)內(nèi)存大小剛好等于2的指數(shù)次冪時(shí)被翻倍。
將log2Group = log2Delta + LOG2_SIZE_CLASS_GROUP
,nDelta=2^LOG2_SIZE_CLASS_GROUP
代入計(jì)算公式,可得
lastSize = 1 << (log2Group + 1)
即x = log2Group + 1
#5
shift, 當(dāng)前在第幾組,從0開(kāi)始(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,這時(shí)shift固定是0。
從sizeClasses方法可以看到,除了第0組,都滿足shift = log2Group - LOG2_QUANTUM - (LOG2_SIZE_CLASS_GROUP - 1)
。
shift << LOG2_SIZE_CLASS_GROUP
就是該組第一個(gè)內(nèi)存塊size的索引
#6
計(jì)算log2Delta
第0組固定是LOG2_QUANTUM
除了第0組,將nDelta = 2^LOG2_SIZE_CLASS_GROUP
代入計(jì)算公式
lastSize = ( 2^LOG2_SIZE_CLASS_GROUP + 2^LOG2_SIZE_CLASS_GROUP ) * (1 << log2Delta)
lastSize = (1 << log2Delta) << LOG2_SIZE_CLASS_GROUP << 1
#7
前面已經(jīng)定位到第幾組了,下面要找到申請(qǐng)內(nèi)存大小應(yīng)分配在該組第幾位
這里要找到比申請(qǐng)內(nèi)存大的最小size。
申請(qǐng)內(nèi)存大小可以理解為上一個(gè)size加上一個(gè)不大于(1 << log2Delta)的值,即
(nDelta - 1 + 2^LOG2_SIZE_CLASS_GROUP) * (1 << log2Delta) + n
, 備注:0 < n <= (1 << log2Delta)
注意,nDelta - 1就是mod
& deltaInverseMask
,將申請(qǐng)內(nèi)存大小最后log2Delta個(gè)bit位設(shè)置為0,可以理解為減去n
>> log2Delta
,右移log2Delta個(gè)bit位,就是除以(1 << log2Delta),結(jié)果就是(nDelta - 1 + 2 ^ LOG2_SIZE_CLASS_GROUP)
& (1 << LOG2_SIZE_CLASS_GROUP) - 1
, 取最后的LOG2_SIZE_CLASS_GROUP個(gè)bit位的值,結(jié)果就是mod
size - 1
,是為了申請(qǐng)內(nèi)存等于內(nèi)存塊size時(shí)避免分配到下一個(gè)內(nèi)存塊size中,即n == (1 << log2Delta)的場(chǎng)景。
疑問(wèn):既然右移log2Delta個(gè)bit位,那為什么前面要將log2Delta個(gè)bit位設(shè)置為0?
第0組由于log2Group等于log2Delta,代入計(jì)算公式如下
1 << log2Delta + (nDelta - 1) * (1 << log2Delta) + n
, 備注:0 < n <= (1 << log2Delta)
nDelta * (1 << log2Delta) + n
所以第0組nDelta從0開(kāi)始,mod = nDelta
pages2pageIdxCompute方法計(jì)算pageIdx邏輯與size2SizeIdx方法類似,只是將LOG2_QUANTUM變量換成了pageShifts,這里不再重復(fù)。
SizeClasses是給PoolArena(內(nèi)存池),PoolChunk(內(nèi)存塊)提供服務(wù)的,建議大家結(jié)合后面分析PoolArena,PoolChunk的文章一起理解。
如果大家對(duì)SizeClasses具體算法不感興趣,只有理解SizeClasses類中利用sizeClasses表格,為PoolArena,PoolChunk提供計(jì)算index,pageIdx索引的方法,也可以幫助大家理解后面解析PoolArena,PoolChunk的文章。
下面貼出sizeClasses完整表格(可復(fù)制到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 |
到此,關(guān)于“Netty源碼解析之如何理解內(nèi)存對(duì)齊類SizeClasses”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。