為了降低分配和釋放內存的開銷,Netty通過ByteBufAllocator實現了ByteBuf的池化,并且可以用來分配我們想要的ByteBuf實例。
ByteBufAllocate有兩大子類:
- PooledByteBufAllocator
- UnpooledByteBufAllocator
UnpooledByteBufAllocator
關于堆內存的分配:
protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity)
: new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
}
UnpooledUnsafeHeapByteBuf是UnpooledHeapByteBuf的子類,只是在獲取字節(jié)的時候底層采用了Unsafe操作。
關于堆外內存的分配:
@Override
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
ByteBuf buf = PlatformDependent.hasUnsafe() ?
UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
return disableLeakDetector ? buf : toLeakAwareBuffer(buf);
}
在UnsafeDirectByteBuf中,有獲取內存地址,memoryAddress = PlatformDependent.directBufferAddress(buffer),然后在獲取的時候通過memoryAddress加上偏移量拿到字節(jié)。
Unsafe的一般是直接操作內存地址,那么相比于非Unsafe的速度應該會更快一些。
PooledByteBufAllocator
Pooled會更復雜一點,看下直接內存分配:
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
// 拿到線程局部緩存,不同的線程在不同的內存堆上分配,
// threadCache是PoolThreadLocalCache,其繼承自ThreadLocal
// PoolThreadCache是后面的重點
PoolThreadCache cache = threadCache.get();
PoolArena<ByteBuffer> directArena = cache.directArena;
// 如果內存中不存在,則新建,否則直接使用內存中的
ByteBuf buf;
if (directArena != null) {
buf = directArena.allocate(cache, initialCapacity, maxCapacity);
} else {
if (PlatformDependent.hasUnsafe()) {
buf = UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
} else {
buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
}
}
return toLeakAwareBuffer(buf);
}
從對象池中拿到PooledByteBuf進行復用。io.netty.buffer.PoolArena#newByteBuf
然后從緩存上進行分配。io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf<T>, int)
從內存堆上進行分配
內存規(guī)格
接著說下Netty內存相關的一些東西
0~512B Tiny
521B~8K small
8K~16M noarmal
16M以上 Huge
8K以下的內存交做subPage,8K的內存叫做Page,16M的內存空間也叫做Chunk
緩存的數據結構
Netty中與緩存相關的數據結構叫做MemoryRegionCache,io.netty.buffer.PoolThreadCache.MemoryRegionCache,它有三部分組成。
- Queue:chunkHandler。
- sizeClass:tiny,small,normal
- size:N*16B,...

PooledThreadLocalCache是每一個線程都會維護的緩存,每個緩存都維護了幾種不同大小的MemoryRegionCache。

命中緩存分配
io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf<T>, int)
在方法中可以看出具體的分配流程。以其里面調用的io.netty.buffer.PoolThreadCache#allocateTiny為例
- allocate(cacheForTiny(area, normCapacity), buf, reqCapacity),首先調用cacheForTiny(area, normCapacity)找到一個對應size的MemoryRegionCache。
- 然后從找到的MemoryRegionCache的Queue中彈出一個Entry給ByteBuf初始化。
- 將彈出的Entry扔到對象池進行復用。io.netty.util.Recycler.Handle#recycle
最后
這是ByteBuf內存分配器常用的內存分配方法,了解下