1. 概述
本文,我们来分享 PooledByteBufAllocator ,基于内存池的 ByteBuf 的分配器。而 PooledByteBufAllocator 的内存池,是基于 Jemalloc 算法进行分配管理,所以在看本文之前,胖友先跳到 《精尽 Netty 源码解析 —— Buffer 之 Jemalloc(一)简介》 ,将 Jemalloc 相关的几篇文章看完,在回到此处。
2. PooledByteBufAllocatorMetric
io.netty.buffer.PooledByteBufAllocatorMetric
,实现 ByteBufAllocatorMetric 接口,PooledByteBufAllocator Metric 实现类。代码如下:
public final class PooledByteBufAllocatorMetric implements ByteBufAllocatorMetric { |
- 每个实现方法,都是调用
allocator
对应的方法。通过 PooledByteBufAllocatorMetric 的封装,可以统一获得 PooledByteBufAllocator Metric 相关的信息。
3. PooledByteBufAllocator
io.netty.buffer.PooledByteBufAllocator
,实现 ByteBufAllocatorMetricProvider 接口,实现 AbstractByteBufAllocator 抽象类,基于内存池的 ByteBuf 的分配器。
3.1 静态属性
/** |
- 静态变量有点多,主要是为 PoolThreadCache 做的默认配置项。读过 《精尽 Netty 源码解析 —— Buffer 之 Jemalloc(六)PoolThreadCache》 的胖友,是不是灰常熟悉。
- 比较有意思的是,
DEFAULT_NUM_HEAP_ARENA
和DEFAULT_NUM_DIRECT_ARENA
变量的初始化,在<1>
处。- 默认情况下,最小值是
NettyRuntime.availableProcessors() * 2
,也就是 CPU 线程数。这样的好处是, 一个 EventLoop 一个 Arena ,避免多线程竞争。更多的讨论,胖友可以看看 https://github.com/netty/netty/issues/3888 。 - 比较有趣的一段是
runtime.maxMemory() / defaultChunkSize / 2 / 3
代码块。其中,/ 2
是为了保证 Arena 不超过内存的一半,而/ 3
是为了每个 Arena 有三个 Chunk 。 - 当然最终取值是上述两值的最小值。所以在推荐上,尽可能配置的内存,能够保证
defaultMinNumArena
。因为要避免多线程竞争。
- 默认情况下,最小值是
3.2 validateAndCalculatePageShifts
#validateAndCalculatePageShifts(int pageSize)
方法,校验 pageSize
参数,并计算 pageShift
值。代码如下:
private static int validateAndCalculatePageShifts(int pageSize) { |
- 默认情况下,
pageSize = 8KB = 8 * 1024= 8096
,pageShift = 8192
。
3.3 validateAndCalculateChunkSize
#validateAndCalculateChunkSize(int pageSize, int maxOrder)
方法,校验 maxOrder
参数,并计算 chunkSize
值。代码如下:
private static int validateAndCalculateChunkSize(int pageSize, int maxOrder) { |
3.4 构造方法
/** |
- orz 代码比较长,主要是构造方法和校验代码比较长。胖友自己耐心看下。笔者下面只重点讲几个属性。
DEFAULT
静态属性,PooledByteBufAllocator 单例。绝绝绝大多数情况下,我们不需要自己创建 PooledByteBufAllocator 对象,而只要使用该单例即可。threadCache
属性,线程变量,用于获得 PoolThreadCache 对象。通过该属性,不同线程虽然使用相同的DEFAULT
单例,但是可以获得不同的 PoolThreadCache 对象。关于 PoolThreadLocalCache 的详细解析,见 「4. PoolThreadLocalCache」 中。#newArenaArray(int size)
方法,创建 PoolArena 数组。代码如下:private static <T> PoolArena<T>[] newArenaArray(int size) {
return new PoolArena[size];
}
3.5 newHeapBuffer
#newHeapBuffer(int initialCapacity, int maxCapacity)
方法,创建 Heap ByteBuf 对象。代码如下:
|
- 代码比较易懂,胖友自己看代码注释。
3.6 newDirectBuffer
#newDirectBuffer(int initialCapacity, int maxCapacity)
方法,创建 Direct ByteBuf 对象。代码如下:
|
- 代码比较易懂,胖友自己看代码注释。
3.6 其它方法
其它方法,主要是 Metric 相关操作为主。这里就不再多做哔哔啦,胖友自己感兴趣的话,可以翻翻噢。
4. PoolThreadLocalCache
PoolThreadLocalCache ,是 PooledByteBufAllocator 的内部类。继承 FastThreadLocal 抽象类,PoolThreadCache ThreadLocal 类。
4.1 构造方法
/** |
4.2 leastUsedArena
#leastUsedArena(PoolArena<T>[] arenas)
方法,从 PoolArena 数组中,获取线程使用最少的 PoolArena 对象,基于 PoolArena.numThreadCaches
属性。通过这样的方式,尽可能让 PoolArena 平均分布在不同线程,从而尽肯能避免线程的同步和竞争问题。代码如下:
private <T> PoolArena<T> leastUsedArena(PoolArena<T>[] arenas) { |
4.3 initialValue
#initialValue()
方法,初始化线程的 PoolThreadCache 对象。代码如下:
|
4.4 onRemoval
#onRemoval(PoolThreadCache threadCache)
方法,释放 PoolThreadCache 对象的缓存。代码如下:
|
666. 彩蛋
推荐阅读文章:
- 杨武兵 《netty源码分析系列——PooledByteBuf&PooledByteBufAllocator》
- wojiushimogui 《Netty源码分析:PooledByteBufAllocator》
- RobertoHuang 《死磕Netty源码之内存分配详解(一)(PooledByteBufAllocator)》