本系列:
前面兩篇分別分析了PoolChunk和PoolSubpage的實現,本文主要分析管理PoolChunk生命週期的PoolChunkList。
PoolChunkList
PoolChunkList負責管理多個chunk的生命週期,在此基礎上對記憶體分配進行進一步的優化。
1 2 3 4 5 6 7 8 9 10 |
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連結串列更後面,具體實現如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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會移動到前一個列表中,實現如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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連結串列中前後移動。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!