死磕 Netty 之内存篇:内存对齐类 SizeClasses
Netty 为了更好地管理内存,它将内存进行规格化,在基于 jemalloc3 版本是将整个内存划分为 Tiny、Small、Normal 和 Huge 四类,而基于 jemalloc4 的 Netty 则将 Tiny 去掉了,保留了 Small、Normal、Huge。那 Netty 是怎么确认申请内存的大小是哪个规格呢?内部又是如何那规划这些内存规格呢?这就要依仗一个极其重要的类了 SizeClass
。为什么说是极其重要呢,看下图吧:
SizeClass
简介
SizeClass 是什么?它在 Netty 内存池中扮演什么角色?
它是 Netty的内存对齐类,为 Netty 内存池中的内存块提供大小对齐,索引计算等服务方法。
它内部维护着一张二维数组表,该数组存储着与内存规格相关的详细信息,我们先看看这张表长什么样(部分):
整个二维数组有 7 列,76 行,每列含义如下:
-
index
:由 0 开始的自增序列号,表示每个 size 类型的索引。 -
log2Group
:表示每个 Size 所在的组,以每 4 行为一组,总共 19 组,第 0 组比较特殊,值为 4,从第 1 组开始,其值为 6 ,后面每组自增 1; -
log2Delta
:表示当前序号所对应的 size 和前一个序号所对应的 size 的差值的 log2 的值。- 比如 index = 4,对应 size 为 80,index = 3 对应 64,所以
log2Delta(4) = log2(80 - 64) = 4
。 - 这里你仔细观察,你会发现从第 1 组开始,
log2Delta
总是比log2Group
小 1,而且在源码中log2Delta
也不是通过复杂的计算方式得到的,而是通过很简单的递增方式。
- 比如 index = 4,对应 size 为 80,index = 3 对应 64,所以
-
nDelta
:表示组内增量的倍数,你可以简单地认为就是每个组内的序号- 比如第 7 组 ,index 对应的( 8、 9、10、 11),对应 nDelta 为 (1 、2 、3、 4),第 0 组依然特殊,它从 0 开始。
-
isMultiPageSize
:表示当前 Size 是否为 PageSize(8KB = 8192) 的整数倍。 -
isSubPage
:表示当前 Size 是否为 SubPage 类型。 -
log2DeltaLookup
:当index <= 27
时,其值和 log2Delta 相等,当index > 27
,其值为 0。
除了维护这个总表(sizeClasses)外,SizeClass 还维护着其他三张表:
sizeIdx2sizeTab
:维护着 index 和 size的关系。pageIdx2sizeTab
:维护着 pageSize 倍数的内存规格表。size2idxTab
:维护着小于等于 4KB(4096) 规格的表。
在下面的源码解析中,大明哥将带你详细来看看这四张表的生成以及使用。
在 SizeClass 的总表 sizeClasses 的 Size 字段由 log2Group、nDelta、log2Delta 三个字段共同决定,其公式如下:
size = 1 << log2Group + nDelta * (1 << log2Delta)
对其进行推导:
从这个公式可以得出两个结论:
-
每一组的 log2Delta 是相同的, nDelta 的取值范围为
[1,4]
,所以4 + nDelta
的取值范围为 [5,8],也就是说同一组的 Size 是按照 {5,6,7,8} 倍来增长的,注意第 0 组是{4,5,6,7} 倍来增长。我们简单来验算下:- 第 1 组{indx = 4 5 6 7},其 log2Delta = 4,导入公式
index4 > size = 2^4*5 = 80
、index4 > size = 2^4*6 = 96
、index4 > size = 2^4*7 = 112
、index4 > size = 2^4*8 = 128
。
- 第 1 组{indx = 4 5 6 7},其 log2Delta = 4,导入公式
-
由于每组都是以固定倍数递增,你会发现 nDelta 相同情况下,后一组是前一组的两倍:
size_后 / size_前 = 2^log2Delta_后*(4 + nDelta) / 2^log2Delta_前*(4 + nDelta) = 2 ^(log2Delta_后 - log2Delta_前) = 2
具体详细的生成过程,下面源码部分讲述。
源码分析
成员变量
SizeClasses 成员变量我们主要理解一些常量就可以了:
// 最小规格的移位
// SizeClass 的最小规格为 16B,所以最小规格偏移量为:log2(16) = 4
static final int LOG2_QUANTUM = 4;
// 每组的移位
// 以每 4 行为一组,所以 log2(4) = 2
private static final int LOG2_SIZE_CLASS_GROUP = 2;
// 定义最大详细划分查找表的规格限制为4096,移位为12
// 因为 size2idxTab 详细划分查找表中,最大限制size大小为4096,所以这里 log2(4096) = 12
private static final int LOG2_MAX_LOOKUP_SIZE = 12;
/**
* 总表 sizeClasses 中的 7 列
*/
// index
private static final int INDEX_IDX = 0;
// log2Group
private static final int LOG2GROUP_IDX = 1;
// log2Delta
private static final int LOG2DELTA_IDX = 2;
// nDelta
private static final int NDELTA_IDX = 3;
// isMultiPageSize
private static final int PAGESIZE_IDX = 4;
// isSubPage
private static final int SUBPAGE_IDX = 5;
// log2DeltaLookup
private static final int LOG2_DELTA_LOOKUP_IDX = 6;
// 一页的大小 默认8KB
protected final int pageSize;
// 一页的移位 默认13
protected final int pageShifts;
// 一chunk的大小 默认16mb
protected final int chunkSize;
// 总规格数量 默认 76
final int nSizes;
// subPage的数量 默认39
int nSubpages;
// 页倍数的规格数量 默认40
int nPSizes;
// 记录最大Subpage的 index 默认是38
int smallMaxSizeIdx;
// 4096
private int lookupMaxSize;
// sizeClasses 总表
private final short[][] sizeClasses;
// 维护着 pageSize 倍数的内存规格表
private final int[] pageIdx2sizeTab;
// 维护着 index 和 size的关系
private final int[] sizeIdx2sizeTab;
// 维护着小于等于 4KB(4096) 规格的表
private final int[] size2idxTab;
构造函数
protected SizeClasses(int pageSize, int pageShifts, int chunkSize, int directMemoryCacheAlignment){
// chunkSize = 4MB = 4194394
// group = log2(4194394) + 1 - 4 = 19
int group = log2(chunkSize) + 1 - LOG2_QUANTUM;
// 初始化总表
// 19 << 2 = 84
// sizeClasses = short[76][7]
short[][] sizeClasses = new short[group << LOG2_SIZE_CLASS_GROUP][7];
int normalMaxSize = -1;
int nSizes = 0;
int size = 0;
int log2Group = LOG2_QUANTUM; // 4
int log2Delta = LOG2_QUANTUM; // 4
int ndeltaLimit = 1 << LOG2_SIZE_CLASS_GROUP; // 4
// 初始化第 0 组
for (int nDelta = 0; nDelta < ndeltaLimit; nDelta++, nSizes++) {
short[] sizeClass = newSizeClass(nSizes, log2Group, log2Delta, nDelta, pageShifts);
sizeClasses[nSizes] = sizeClass;
size = sizeOf(sizeClass, directMemoryCacheAlignment);
}
// 第 1 组,log2Group 从 6 开始
log2Group += LOG2_SIZE_CLASS_GROUP;
//从第 1 组开始,初始化整个 sizeClasses 表
// log2Group 和 log2Delta 并没有通过复杂的计算来得到,而是采用非常简单的递增方式来实现
// 从第 1 组开始,log2Group 从 6 开始,log2Delta 从 4 开始
for (; size < chunkSize; log2Group++, log2Delta++) {
// nDelta <= ndeltaLimit --> 限定每组个数为 4
for (int nDelta = 1; nDelta <= ndeltaLimit && size < chunkSize; nDelta++, nSizes++) {
short[] sizeClass = newSizeClass(nSizes, log2Group, log2Delta, nDelta, pageShifts);
sizeClasses[nSizes] = sizeClass;
size = normalMaxSize = sizeOf(sizeClass, directMemoryCacheAlignment);
}
}
// 断言判断 最后生成的 size值是否符合 chunkSize
assert chunkSize == normalMaxSize;
int smallMaxSizeIdx = 0;
int lookupMaxSize = 0;
int nPSizes = 0;
int nSubpages = 0;
for (int idx = 0; idx < nSizes; idx++) {
short[] sz = sizeClasses[idx];
// 是否为 pageSize 的整数倍
if (sz[PAGESIZE_IDX] == yes) {
nPSizes++;
}
// 是否为 subPage
if (sz[SUBPAGE_IDX] == yes) {
nSubpages++;
smallMaxSizeIdx = idx;
}
// lookupMaxSize 记录值 = 4096
if (sz[LOG2_DELTA_LOOKUP_IDX] != no) {
lookupMaxSize = sizeOf(sz, directMemoryCacheAlignment);
}
}
this.smallMaxSizeIdx = smallMaxSizeIdx;
this.lookupMaxSize = lookupMaxSize;
this.nPSizes = nPSizes;
this.nSubpages = nSubpages;
this.nSizes = nSizes;
this.pageSize = pageSize;
this.pageShifts = pageShifts;
this.chunkSize = chunkSize;
this.directMemoryCacheAlignment = directMemoryCacheAlignment;
// 生成 index 和 size的关系
sizeIdx2sizeTab = newIdx2SizeTab(sizeClasses, nSizes, directMemoryCacheAlignment);
// 生成 pageSize 倍数的内存规格表
pageIdx2sizeTab = newPageIdx2sizeTab(sizeClasses, nSizes, nPSizes, directMemoryCacheAlignment); // 生成小于等于 4KB(4096) 规格的表
size2idxTab = newSize2idxTab(lookupMaxSize, sizeClasses);
}
注意:由于大明哥使用的 Netty 版本为 4.1.77.Final,chunkSize 默认为 4MB,而不是大部分网上教程的 16MB。
这里有几个核心方法需要来详细阐述:
newSizeClass()
:生成总表 sizeClasses 的某行数据。sizeOf()
:计算 size。newIdx2SizeTab()
:生成 sizeIdx2sizeTab 表。newPageIdx2sizeTab()
:生成 pageIdx2sizeTab 表。newSize2idxTab()
:生成 size2idxTab 表。
下面就一个方法一个方法的看吧。
newSizeClass() - 生成总表 sizeClasses 的行
private static short[] newSizeClass(int index, int log2Group, int log2Delta, int nDelta, int pageShifts) {
// 是否为 pageSize 的整数倍
short isMultiPageSize;
// 大于说明 size 计算的最小单位都是大于 pageSize
if (log2Delta >= pageShifts) {
isMultiPageSize = yes;
} else {
int pageSize = 1 << pageShifts;
int size = calculateSize(log2Group, nDelta, log2Delta);
isMultiPageSize = size == size / pageSize * pageSize? yes : no;
}
// 计算 log2Delta
int log2Ndelta = nDelta == 0? 0 : log2(nDelta);
byte remove = 1 << log2Ndelta < nDelta? yes : no;
// log2(size) --- ①
int log2Size = log2Delta + log2Ndelta == log2Group? log2Group + 1 : log2Group;
if (log2Size == log2Group) {
remove = yes;
}
// pageShifts + LOG2_SIZE_CLASS_GROUP = 15
// 若 size < 2^15 = 32K 即为 subpage
short isSubpage = log2Size < pageShifts + LOG2_SIZE_CLASS_GROUP? yes : no;
// 当 size <= 4096(4K) 即为 log2Delta,否则为 0
int log2DeltaLookup = log2Size < LOG2_MAX_LOOKUP_SIZE ||
log2Size == LOG2_MAX_LOOKUP_SIZE && remove == no
? log2Delta : no;
// 生成行
return new short[] {
(short) index, (short) log2Group, (short) log2Delta,
(short) nDelta, isMultiPageSize, isSubpage, (short) log2DeltaLookup
};
}
这段代码还是比较简单的,这里对 log2Size = log2(size)
进行了一次取巧。
在构造函数中,经过两个 for 循环就可以得到一个完整的 sizeClasses,下面大明哥把这个二维数组详情列出来:
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 | 0 | 0 | 0 | 0 | 0 | 0 | 5242880 | 5M |
69 | 0 | 0 | 0 | 0 | 0 | 0 | 6291456 | 6M |
70 | 0 | 0 | 0 | 0 | 0 | 0 | 7340032 | 7M |
71 | 0 | 0 | 0 | 0 | 0 | 0 | 8388608 | 8M |
72 | 0 | 0 | 0 | 0 | 0 | 0 | 10485760 | 10M |
73 | 0 | 0 | 0 | 0 | 0 | 0 | 12582912 | 12M |
74 | 0 | 0 | 0 | 0 | 0 | 0 | 14680064 | 14M |
75 | 0 | 0 | 0 | 0 | 0 | 0 | 16777216 | 16M |
再次注意:因为大明哥的版本是 4.1.77.final ,chunkSize 默认为 4MB,所以只到 index = 67
sizeOf() - 计算 size
sizeOf()
就是根据公式 size = 1 << log2Group + nDelta * (1 << log2Delta)
来计算 size,如下:
private static int sizeOf(short[] sizeClass, int directMemoryCacheAlignment) {
int log2Group = sizeClass[LOG2GROUP_IDX];
int log2Delta = sizeClass[LOG2DELTA_IDX];
int nDelta = sizeClass[NDELTA_IDX];
int size = calculateSize(log2Group, nDelta, log2Delta);
return alignSizeIfNeeded(size, directMemoryCacheAlignment);
}
private static int calculateSize(int log2Group, int nDelta, int log2Delta) {
return (1 << log2Group) + (nDelta << log2Delta);
}
// 内存对齐,这里一般都是直接返回 size
private static int alignSizeIfNeeded(int size, int directMemoryCacheAlignment) {
if (directMemoryCacheAlignment <= 0) {
return size;
}
int delta = size & directMemoryCacheAlignment - 1;
return delta == 0? size : size + directMemoryCacheAlignment - delta;
}
newIdx2SizeTab() - 生成 sizeIdx2sizeTab 表
newIdx2SizeTab()
用于生成 Idx2SizeTab 表,该表用于维护 index 和 size之间的关系对应关系。
private static int[] newIdx2SizeTab(short[][] sizeClasses, int nSizes, int directMemoryCacheAlignment) {
int[] sizeIdx2sizeTab = new int[nSizes];
for (int i = 0; i < nSizes; i++) {
short[] sizeClass = sizeClasses[i];
sizeIdx2sizeTab[i] = sizeOf(sizeClass, directMemoryCacheAlignment);
}
return sizeIdx2sizeTab;
}
注意这里的 nSizes 为 68 ,不是 76。生成的 Idx2SizeTab 表格如下:
index | size(虚) | Unit |
---|---|---|
0 | 16 | |
1 | 32 | |
2 | 48 | |
3 | 64 | |
4 | 80 | |
5 | 96 | |
6 | 112 | |
7 | 128 | |
8 | 160 | |
9 | 192 | |
10 | 224 | |
11 | 256 | |
12 | 320 | |
13 | 384 | |
14 | 448 | |
15 | 512 | |
16 | 640 | |
17 | 768 | |
18 | 896 | |
19 | 1024 | |
20 | 1280 | |
21 | 1536 | |
22 | 1792 | |
23 | 2048 | |
24 | 2560 | |
25 | 3072 | |
26 | 3584 | |
27 | 4096 | 4KB |
28 | 5120 | |
29 | 6144 | |
30 | 7168 | |
31 | 8192 | 8KB |
32 | 10240 | 10KB |
33 | 12288 | 12KB |
34 | 14336 | 14KB |
35 | 16384 | 16KB |
36 | 20480 | 20KB |
37 | 24576 | 24KB |
38 | 28672 | 28KB |
39 | 32768 | 32KB |
40 | 40960 | 40KB |
41 | 49152 | 48KB |
42 | 57344 | 56KB |
43 | 65536 | 64KB |
44 | 81920 | 80KB |
45 | 98304 | 96KB |
46 | 114688 | 112KB |
runSize | 131072 | 128KB |
48 | 163840 | 160KB |
49 | 196608 | 192KB |
50 | 229376 | 224KB |
51 | 262144 | 256KB |
52 | 327680 | 320KB |
53 | 393216 | 384KB |
54 | 458752 | 448KB |
55 | 524288 | 512KB |
56 | 655360 | 640KB |
57 | 786432 | 768KB |
58 | 917504 | 896KB |
59 | 1048576 | 1.0MB |
60 | 1310720 | 1.25MB |
61 | 1572864 | 1.5MB |
62 | 1835008 | 1.75MB |
63 | 2097152 | 2MB |
64 | 2621440 | 2.5MB |
65 | 3145728 | 3MB |
66 | 3670016 | 3.5MB |
67 | 4194304 | 4MB |
newPageIdx2sizeTab() - 生成 pageIdx2sizeTab 表
newPageIdx2sizeTab()
用于生成pageIdx2sizeTab 表,该表维护着 pageSize 倍数的内存规格表:
private static int[] newPageIdx2sizeTab(short[][] sizeClasses, int nSizes, int nPSizes,
int directMemoryCacheAlignment) {
int[] pageIdx2sizeTab = new int[nPSizes];
int pageIdx = 0;
for (int i = 0; i < nSizes; i++) {
short[] sizeClass = sizeClasses[i];
// 只要 pageSize 整数倍表
if (sizeClass[PAGESIZE_IDX] == yes) {
pageIdx2sizeTab[pageIdx++] = sizeOf(sizeClass, directMemoryCacheAlignment);
}
}
return pageIdx2sizeTab;
}
其实就是去 sizeClasses 总表中 isMultiPageSize 为 1 的行。生成的表如下:
pageIndex | size | Unit | sizeClasses 中 index |
---|---|---|---|
0 | 8192 | 8KB | 31 |
1 | 16384 | 16KB | 35 |
2 | 24576 | 24KB | 37 |
3 | 32768 | 32KB | 39 |
4 | 40960 | 40KB | 40 |
5 | 49152 | 48KB | 41 |
6 | 57344 | 56KB | 42 |
7 | 65536 | 64KB | 43 |
8 | 81920 | 80KB | 44 |
9 | 98304 | 96KB | 45 |
10 | 114688 | 112KB | 46 |
11 | 131072 | 128KB | 47 |
12 | 163840 | 160KB | 48 |
13 | 196608 | 192KB | 49 |
14 | 229376 | 224KB | 50 |
15 | 262144 | 256KB | 51 |
16 | 327680 | 320KB | 52 |
17 | 393216 | 384KB | 53 |
18 | 458752 | 448KB | 54 |
19 | 524288 | 512KB | 55 |
20 | 655360 | 640KB | 56 |
21 | 786432 | 768KB | 57 |
22 | 917504 | 896KB | 58 |
23 | 1048576 | 1.0MB | 59 |
24 | 1310720 | 1.25MB | 60 |
25 | 1572864 | 1.5MB | 61 |
26 | 1835008 | 1.75MB | 62 |
27 | 2097152 | 2MB | 63 |
28 | 2621440 | 2.5MB | 64 |
29 | 3145728 | 3MB | 65 |
30 | 3670016 | 3.5MB | 66 |
31 | 4194304 | 4MB | 67 |
newSize2idxTab() - 生成 size2idxTab 表
newSize2idxTab()
用于生成 size2idxTab 表,该表维护着小于等于 4KB(4096) 规格的表:
private static int[] newSize2idxTab(int lookupMaxSize, short[][] sizeClasses) {
int[] size2idxTab = new int[lookupMaxSize >> LOG2_QUANTUM];
int idx = 0;
int size = 0;
// 遍历规格小于 4096 的
for (int i = 0; size <= lookupMaxSize; i++) {
int log2Delta = sizeClasses[i][LOG2DELTA_IDX];
// 目的是 求出 该规格 可以按照16划分多少份
int times = 1 << log2Delta - LOG2_QUANTUM;
// 生成表
while (size <= lookupMaxSize && times-- > 0) {
size2idxTab[idx++] = i;
size = idx + 1 << LOG2_QUANTUM;
}
}
return size2idxTab;
}
生成的表如下:
idx | index | size |
---|---|---|
0 | 0 | 16B |
1 | 1 | 32B |
2 | 2 | 48B |
3 | 3 | 64B |
4 | 4 | 80B |
5 | 5 | 96B |
6 | 6 | 112B |
7 | 7 | 128B |
8 | 8 | 144B |
9 | 9 | 160B |
10 | 9 | 176B |
... | .... | ... |
250 | 27 | 4016B |
251 | 27 | 4032B |
252 | 27 | 4048B |
253 | 27 | 4064B |
254 | 27 | 4080B |
255 | 27 | 4096B |
到这了 SizeClass 分析完了,这篇文章最核心的就在于那四张表的生成,生成过程其实不复杂,就要是要理解这个过程,还有几个查询方法大明哥没有分析,留给后面在讲内存分配的时候再讲会更加合适。
转载自:https://juejin.cn/post/7385430107291238411