前面我们说到了Netty的内存管理机制,也讨论了PoolChunk的内存分配算法,但是PoolChunk只有16M,这远远不够用,所以会有很多很多PoolChunk,这些PoolChunk组成一个链表,然后用PoolChunkList持有这个链表。下面就来介绍一下PoolChunkList。
先看一下PoolChunkList有哪些属性
/**
* 所属 PoolArena 对象
*/
private final PoolArena<T> arena;
/**
* 下一个 PoolChunkList 对象
*/
private final PoolChunkList<T> nextList;
/**
* Chunk 最小内存使用率
*/
private final int minUsage;
/**
* Chunk 最大内存使用率
*/
private final int maxUsage;
/**
* 每个 Chunk 最大可分配的容量
*
* @see #calculateMaxCapacity(int, int) 方法
*/
private final int maxCapacity;
/**
* PoolChunk 头节点
*/
private PoolChunk<T> head;
/**
* 前一个 PoolChunkList 对象
*/
// This is only update once when create the linked like list of PoolChunkList in PoolArena constructor.
private PoolChunkList<T> prevList;
PoolChunkList包含了一个prev和一个next,组成了一个链表。既然按使用率分配,那么PoolChunk在使用过程中是会动态变化的,所以PoolChunk会在不同PoolChunkList中变化。同时申请空间,使用哪一个PoolChunkList也是有先后顺序的。下面我们来看看PoolChunkList是怎么分配内存的。
boolean allocate(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {
// 双向链表中无 Chunk
// 申请分配的内存超过 ChunkList 的每个 Chunk 最大可分配的容量
if (head == null || normCapacity > maxCapacity) {
// Either this PoolChunkList is empty or the requested capacity is larger then the capacity which can
// be handled by the PoolChunks that are contained in this PoolChunkList.
return false;
}
// 遍历双向链表。注意,遍历的是 ChunkList 的内部双向链表。
for (PoolChunk<T> cur = head;;) {
// 分配内存块
long handle = cur.allocate(normCapacity);
// 分配失败
if (handle < 0) {
// 进入下一节点
cur = cur.next;
// 若下一个节点不存在,返回 false ,结束循环
if (cur == null) {
return false; // 分配失败
}
// 分配成功
} else {
// 初始化内存块到 PooledByteBuf 对象中
cur.initBuf(buf, handle, reqCapacity);
// 超过当前 ChunkList 管理的 Chunk 的内存使用率上限
if (cur.usage() >= maxUsage) {
// 从当前 ChunkList 节点移除
remove(cur);
// 添加到下一个 ChunkList 节点
nextList.add(cur);
}
return true; // 分配成功
}
}
}
PoolChunkList的分析就到这里了。