文章目录
1. PooledByteBufAllocator
1.1 PooledByteBufAllocator的初始化
分配器分配内存的API,在文章(三) netty非池化内存的分配与回收:UnpooledByteBufAllocator的前半部分有说明,可以参考。
PooledByteBufAllocator主要属性有三个:
private final PoolArena<byte[]>[] heapArenas;
private final PoolArena<ByteBuffer>[] directArenas;
private final PoolThreadLocalCache threadCache;
heapArenas是堆内存的表示,每个arena内存以字节数组byte[]表示,注意arena不会直接持有内存,arena所有内存通过PoolChunk持有 。directArenas是直接内存的表示,每个arena的内存以DirectByteBuffer表示。threadCache是FastThreadLocal的子类对象。
这三个属性都在创建PooledByteBufAllocator对象时初始化:
==============构造器代码片段=============
// 初始化ThreadLocal
threadCache = new PoolThreadLocalCache(useCacheForAllThreads);
// 初始化heapArenas
heapArenas = newArenaArray(nHeapArena);
for (int i = 0; i < heapArenas.length; i ++) {
PoolArena.HeapArena arena = new PoolArena.HeapArena(this,
pageSize, maxOrder, pageShifts, chunkSize,
directMemoryCacheAlignment);
heapArenas[i] = arena;
}
// 初始化directArenas
directArenas = newArenaArray(nDirectArena);
for (int i = 0; i < directArenas.length; i ++) {
PoolArena.DirectArena arena = new PoolArena.DirectArena(
this, pageSize, maxOrder, pageShifts, chunkSize, directMemoryCacheAlignment);
directArenas[i] = arena;
}
1.2 PooledByteBufAllocator的内存分配
分配堆内存
当调用PooledByteBufAllocator.heapBuffer(int initialCapacity)分配内存时,之后会调用到newHeapBuffer(int initialCapacity, int maxCapacity)来分配,newHeapBuffer在PooledByteBufAllocator中的逻辑是:
-
从实例变量threadCache获取分配缓冲
这里的要点是:netty每个线程都有自己的分配缓冲。线程第一次分配内存时,首先会给线程绑一个arena,用于后续内存的分配。arena获取的逻辑是最少持有线程数:arena会有个计数器,记录当前持有该arena的线程的个数,每次给线程分配arena时,分配持有线程数最少的arena。分配到arena后,会封装为PoolThreadCache对象,放到ThreadLocal中。后续每次分配内存,先从缓冲分配,缓冲分配不了,再从arena分配可用的内存。 -
调用arena.allocate方法进行内存的分配。
分配直接内存
整个过程,和分配堆内存一样,不在赘述。
2. 内存池字节缓冲的表示
内存池的场景,主要有PooledHeapByteBuf和PooledDirectByteBuf来表示分配到的字节缓冲。
它们都是PoolChunk分配到的一大段连续内存上的一小段,通过下标来控制访问范围。它们的主要属性在父类PooledByteBuf中:
protected T memory;
protected int offset;
protected int length;
假设从同一个chunk上分配了两个堆字节缓冲,偏移量分别是(offset=16 bytes,length=16 bytes)和(offset=32 bytes,length=16 bytes),那么他们是一大段连续内存上的两个片段,如图所示: