前面我们介绍了PoolChunk和PoolChunkList,对于小内存(小于4096)的分配还会将Page细化成更小的单位Subpage。Subpage按大小分有两大类,tiny和small。下面就来介绍一下PoolSubpage。
先来看一下PoolSubpage有哪些属性
/**
* 所属 PoolChunk 对象
*/
final PoolChunk<T> chunk;
/**
* 在 {@link PoolChunk#memoryMap} 的节点编号
*/
private final int memoryMapIdx;
/**
* 在 Chunk 中,偏移字节量
*
* @see PoolChunk#runOffset(int)
*/
private final int runOffset;
/**
* Page 大小 {@link PoolChunk#pageSize}
*/
private final int pageSize;
/**
* Subpage 分配信息数组
*
* 每个 long 的 bits 位代表一个 Subpage 是否分配。
* 因为 PoolSubpage 可能会超过 64 个( long 的 bits 位数 ),所以使用数组。
* 例如:Page 默认大小为 8KB ,Subpage 默认最小为 16 B ,所以一个 Page 最多可包含 8 * 1024 / 16 = 512 个 Subpage 。
* 因此,bitmap 数组大小为 512 / 64 = 8 。
* 另外,bitmap 的数组大小,使用 {@link #bitmapLength} 来标记。或者说,bitmap 数组,默认按照 Subpage 的大小为 16B 来初始化。
* 为什么是这样的设定呢?因为 PoolSubpage 可重用,通过 {@link #init(PoolSubpage, int)} 进行重新初始化。
*/
private final long[] bitmap;
/**
* 双向链表,前一个 PoolSubpage 对象
*/
PoolSubpage<T> prev;
/**
* 双向链表,后一个 PoolSubpage 对象
*/
PoolSubpage<T> next;
/**
* 是否未销毁
*/
boolean doNotDestroy;
/**
* 每个 Subpage 的占用内存大小
*/
int elemSize;
/**
* 总共 Subpage 的数量
*/
private int maxNumElems;
/**
* {@link #bitmap} 长度
*/
private int bitmapLength;
/**
* 下一个可分配 Subpage 的数组位置
*/
private int nextAvail;
/**
* 剩余可用 Subpage 的数量
*/
private int numAvail;
PoolSubpage有一个PoolSubpage池,第一次申请小内存空间的时候,需要先申请一个空闲页,然后将该页转成PoolSubpage,再将该页设为已被占用,最后再把这个PoolSubpage存到PoolSubpage池中。这样下次就不需要再去申请空闲页了,直接去池中找就好了。Netty中有36种PoolSubpage,所以用36个PoolSubpage链表表示PoolSubpage池。再来看看PoolSubpage怎么分配内存的
long allocate() {
if (elemSize == 0) {
return toHandle(0);
}
if (numAvail == 0 || !doNotDestroy) {
return -1;
}
// 获得下一个可用的 Subpage 在 bitmap 中的总体位置
final int bitmapIdx = getNextAvail();
// 获得下一个可用的 Subpage 在 bitmap 中数组的位置
int q = bitmapIdx >>> 6;
// 获得下一个可用的 Subpage 在 bitmap 中数组的位置的第几 bits
int r = bitmapIdx & 63;
assert (bitmap[q] >>> r & 1) == 0;
// 修改 Subpage 在 bitmap 中不可分配。
bitmap[q] |= 1L << r;
// 可用 Subpage 内存块的计数减一
if (-- numAvail == 0) { // 无可用 Subpage 内存块
// 从双向链表中移除
removeFromPool();
}
// 计算 handle
return toHandle(bitmapIdx);
}
PoolSubpage就分析到这里了。
网友评论