前面兩篇分別分析了PoolChunk和PoolSubpage的實現,本文主要分析管理PoolChunk生命周期的PoolChunkList。
1、深入淺出Netty內存管理 PoolChunk
2、深入淺出Netty內存管理 PoolSubpage
PoolChunkList
PoolChunkList負責管理多個chunk的生命周期,在此基礎上對內存分配進行進一步的優化。
final class PoolChunkList<T> implements PoolChunkListMetric {
private final PoolChunkList<T> nextList;
private final int minUsage;
private final int maxUsage;
private PoolChunk<T> head;
private PoolChunkList<T> prevList;
...
}
從代碼實現可以看出,每個PoolChunkList實例維護了一個PoolChunk鏈表,自身也形成一個鏈表,為何要這么實現?
隨著chunk中page的不斷分配和釋放,會導致很多碎片內存段,大大增加了之后分配一段連續內存的失敗率,針對這種情況,可以把內存使用率較大的chunk放到PoolChunkList鏈表更后面,具體實現如下:
boolean allocate(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {
if (head == null) {
return false;
}
for (PoolChunk<T> cur = head;;) {
long handle = cur.allocate(normCapacity);
if (handle < 0) {
cur = cur.next;
if (cur == null) {
return false;
}
} else {
cur.initBuf(buf, handle, reqCapacity);
if (cur.usage() >= maxUsage) { // (1)
remove(cur);
nextList.add(cur);
}
return true;
}
}
}
假設poolChunkList中已經存在多個chunk。當分配完內存后,如果當前chunk的使用量超過maxUsage,則把該chunk從當前鏈表中刪除,添加到下一個鏈表中。
但是,隨便chunk中內存的釋放,其內存使用率也會隨著下降,當下降到minUsage時,該chunk會移動到前一個列表中,實現如下:
boolean free(PoolChunk<T> chunk, long handle) {
chunk.free(handle);
if (chunk.usage() < minUsage) {
remove(chunk);
if (prevList == null) {
assert chunk.usage() == 0;
return false;
} else {
prevList.add(chunk);
return true;
}
}
return true;
}
從poolChunkList的實現可以看出,每個chunkList的都有一個上下限:minUsage和maxUsage,兩個相鄰的chunkList,前一個的maxUsage和后一個的minUsage必須有一段交叉值進行緩沖,否則會出現某個chunk的usage處于臨界值,而導致不停的在兩個chunk間移動。
所以chunk的生命周期不會固定在某個chunkList中,隨著內存的分配和釋放,根據當前的內存使用率,在chunkList鏈表中前后移動。
END。
我是占小狼。
在魔都艱苦奮斗,白天是上班族,晚上是知識服務工作者。
如果讀完覺得有收獲的話,記得關注和點贊哦。
非要打賞的話,我也是不會拒絕的。