Netty从0到1系列之Recycler对象池技术【3】
文章目录
- Recycler 对象池技术【3】
- 三、Recycler 源码解析【4.1.112.Final】
- 3.1 核心设计思想
- 3.2 核心类结构
- 3.3 Recycler<T> 主类结构
- 3.4 配置与初始化模块 - 系统属性配置和静态初始化
- 3.5 线程本地存储模块
- 3.6 对象获取与创建模块
- 3.7 对象回收模块
推荐阅读:
【01】Netty从0到1系列之I/O模型
【02】Netty从0到1系列之NIO
【03】Netty从0到1系列之Selector
【04】Netty从0到1系列之Channel
【05】Netty从0到1系列之Buffer(上)
【06】Netty从0到1系列之Buffer(下)
【07】Netty从0到1系列之零拷贝技术
【08】Netty从0到1系列之整体架构、入门程序
【09】Netty从0到1系列之EventLoop
【10】Netty从0到1系列之EventLoopGroup
【11】Netty从0到1系列之Future
【12】Netty从0到1系列之Promise
【13】Netty从0到1系列之Netty Channel
【14】Netty从0到1系列之ChannelFuture
【15】Netty从0到1系列之CloseFuture
【16】Netty从0到1系列之Netty Handler
【17】Netty从0到1系列之Netty Pipeline【上】
【18】Netty从0到1系列之Netty Pipeline【下】
【19】Netty从0到1系列之Netty ByteBuf【上】
【20】Netty从0到1系列之Netty ByteBuf【中】
【21】Netty从0到1系列之Netty ByteBuf【下】
【22】Netty从0到1系列之Netty 逻辑架构【上】
【23】Netty从0到1系列之Netty 逻辑架构【下】
【24】Netty从0到1系列之Netty 启动细节分析
【25】Netty从0到1系列之Netty 线程模型【上】
【26】Netty从0到1系列之Netty 线程模型【下】
【27】Netty从0到1系列之Netty ChannelPipeline
【28】Netty从0到1系列之Netty ChannelHandler
【29】Netty从0到1系列之Netty拆包、粘包【1】
【30】Netty从0到1系列之Netty拆包、粘包【2】
【31】Netty从0到1系列之Netty拆包、粘包【3】
【32】Netty从0到1系列之Netty拆包、粘包【4】
【33】Netty从0到1系列之Netty拆包、粘包【5】
【34】Netty从0到1系列之动态从内存分配】
【35】Netty从0到1系列之writeAndFlush原理分析】
【36】Netty从0到1系列之Netty内存管理【上】】
【37】Netty从0到1系列之Netty内存管理【下】】
【38】Netty从0到1系列之Netty内存管理【1】】
【39】Netty从0到1系列之Netty内存管理【2】】
Recycler 对象池技术【3】
三、Recycler 源码解析【4.1.112.Final】
3.1 核心设计思想
Recycler 是一个轻量级的对象池实现,主要目的是减少频繁创建和销毁对象带来的垃圾回收压力。它采用了线程本地存储和栈式管理的方式,为每个线程维护一个独立的对象池.
3.2 核心类结构
- 配置与初始化模块 - 系统属性配置和静态初始化
- 线程本地存储模块 -
FastThreadLocal<LocalPool<T>>
管理每个线程的对象池 - 对象获取与创建模块 -
get()
方法实现对象的获取逻辑 - 对象回收模块 -
Handle.recycle()
和LocalPool.release()
实现对象回收 - 本地池管理模块 -
LocalPool<T>
管理单个线程的对象池 - 句柄管理模块 -
DefaultHandle<T>
管理对象的生命周期状态
3.3 Recycler 主类结构
Recycler是对象池的入口点,负责全局配置和线程本地池的管理。
public abstract class Recycler<T> {// 静态配置参数private static final int DEFAULT_MAX_CAPACITY_PER_THREAD;private static final int RATIO;private static final int DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD;private static final boolean BLOCKING_POOL;private static final boolean BATCH_FAST_TL_ONLY;// 线程本地池private final FastThreadLocal<LocalPool<T>> threadLocal = new FastThreadLocal<LocalPool<T>>() {@Overrideprotected LocalPool<T> initialValue() {return new LocalPool<T>(maxCapacityPerThread, interval, chunkSize);}};// 获取对象的核心方法@SuppressWarnings("unchecked")public final T get() {if (maxCapacityPerThread == 0) {return newObject((Handle<T>) NOOP_HANDLE);}LocalPool<T> localPool = threadLocal.get();DefaultHandle<T> handle = localPool.claim();// ... 处理handle获取逻辑}
}
配置参数解析:
- maxCapacityPerThread: 每个线程池的最大容量,默认4K
- RATIO: 回收比例,控制对象回收频率,默认8(每8次尝试回收1次)
- chunkSize: 每个Link存储的对象数量,默认32
- BLOCKING_POOL: 是否使用阻塞模式,默认false
- BATCH_FAST_TL_ONLY: 是否只在FastThreadLocalThread上启用批处理,默认true
3.4 配置与初始化模块 - 系统属性配置和静态初始化
✅ 1. 工作原理
该模块在类加载时通过静态代码块初始化全局配置参数,这些参数可以通过系统属性进行定制:
static {// 1. 初始化单线程最大容量(优先级:io.netty.recycler.maxCapacityPerThread > io.netty.recycler.maxCapacity)int maxCapacityPerThread = SystemPropertyUtil.getInt("io.netty.recycler.maxCapacityPerThread",SystemPropertyUtil.getInt("io.netty.recycler.maxCapacity", DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD));if (maxCapacityPerThread < 0) {maxCapacityPerThread = DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD; // 负数时用默认值}DEFAULT_MAX_CAPACITY_PER_THREAD = maxCapacityPerThread;// 2. 初始化批量块大小(每次跨线程转移的最大对象数,默认32)DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD = SystemPropertyUtil.getInt("io.netty.recycler.chunkSize", 32);// 3. 初始化复用比例(默认8:每8次尝试创建Handle,仅1次成功,避免突发分配导致池溢出)RATIO = max(0, SystemPropertyUtil.getInt("io.netty.recycler.ratio", 8));// 4. 调试开关:是否启用阻塞式池(默认false,生产环境禁用)BLOCKING_POOL = SystemPropertyUtil.getBoolean("io.netty.recycler.blocking", false);// 5. 优化开关:是否仅允许FastThreadLocalThread使用批量缓存(默认true,减少非优化线程的开销)BATCH_FAST_TL_ONLY = SystemPropertyUtil.getBoolean("io.netty.recycler.batchFastThreadLocalOnly", true);// 日志输出配置(调试用)if (logger.isDebugEnabled()) { /* 省略日志代码 */ }
}
✅ 2. 核心配置参数
- DEFAULT_MAX_CAPACITY_PER_THREAD: 每个线程默认最大容量(默认4096)
- RATIO: 回收比率,控制新对象创建的频率(默认8,即每8次尝试创建1个新对象)
- DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD: 批量处理的块大小(默认32)
- BLOCKING_POOL: 是否使用阻塞队列(默认false)
- BATCH_FAST_TL_ONLY: 是否仅对FastThreadLocalThread线程启用批处理优化(默认true)
✅ 3. 核心作用
- 动态配置:通过 JVM 参数(如
-Dio.netty.recycler.maxCapacityPerThread=8192
)调整池大小,无需改代码; - 环境适配:
BLOCKING_POOL
用于调试(同步锁保证线程安全,便于排查问题),生产环境用无锁 MPSC 队列; - 安全兜底:参数合法性校验(如最大容量不能小于 4),避免配置错误导致的异常。
这些配置允许用户根据应用场景调整对象池的行为,平衡内存使用和性能。
3.5 线程本地存储模块
FastThreadLocal<LocalPool<T>>
管理每个线程的对象池
✅ 1. 核心代码
private final int maxCapacityPerThread; // 当前Recycler实例的单线程最大容量
private final int interval; // 复用比例间隔(=RATIO,控制Handle创建频率)
private final int chunkSize; // 批量块大小(=DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD)
// 线程本地池:每个线程拥有独立的LocalPool,通过FastThreadLocal实现线程隔离
private final FastThreadLocal<LocalPool<T>> threadLocal = new FastThreadLocal<LocalPool<T>>() {@Overrideprotected LocalPool<T> initialValue() {// 线程首次获取时,创建专属LocalPoolreturn new LocalPool<T>(maxCapacityPerThread, interval, chunkSize);}@Overrideprotected void onRemoval(LocalPool<T> value) throws Exception {// 线程销毁时清理LocalPool(避免内存泄漏)MessagePassingQueue<DefaultHandle<T>> handles = value.pooledHandles;value.pooledHandles = null;value.owner = null;handles.clear(); // 清空缓存的对象,让GC回收}
};
✅ 2. 工作原理
- 使用
FastThreadLocal
(Netty优化的ThreadLocal)为每个线程维护一个独立的LocalPool<T>
实例 initialValue()
方法在首次访问时创建新的本地池onRemoval()
方法在线程被移除时清理资源,避免内存泄漏- 每个线程拥有自己独立的对象池,避免了多线程竞争,提高了性能
✅ 3. 优势
- 无锁设计: 每个线程操作自己的对象池,无需加锁
- 高性能: FastThreadLocal比标准ThreadLocal更快
- 内存隔离: 线程间对象不共享,避免了跨线程传递对象的复杂性
3.6 对象获取与创建模块
get()
方法实现对象的获取逻辑
✅ 1. 核心方法 - get()
get()
是用户获取对象的唯一入口,逻辑优先级:复用池内对象 > 创建新 Handle > 直接创建无池化对象
public final T get() {// 1. 禁用池化(maxCapacityPerThread=0):直接创建无池化对象(用NOOP_HANDLE,回收无效)if (maxCapacityPerThread == 0) {return newObject((Handle<T>) NOOP_HANDLE);}// 2. 获取当前线程的LocalPool(线程私有,无竞争)LocalPool<T> localPool = threadLocal.get();// 3. 尝试从LocalPool认领可用对象DefaultHandle<T> handle = localPool.claim();T obj;if (handle == null) {// 3.1 无可用对象:尝试创建新Handle(受复用比例控制)handle = localPool.newHandle();if (handle != null) {// 3.1.1 创建新对象并绑定到Handle(后续回收时通过Handle归池)obj = newObject(handle);handle.set(obj);} else {// 3.1.2 无法创建Handle(复用比例未满足):直接创建无池化对象obj = newObject((Handle<T>) NOOP_HANDLE);}} else {// 3.2 有可用对象:直接从Handle中获取复用对象obj = handle.get();}return obj;
}
✅ 2. 工作原理
- 禁用检查: 如果
maxCapacityPerThread == 0
,直接创建新对象并返回(不参与回收) - 获取本地池: 通过
threadLocal.get()
获取当前线程的本地池 - 尝试获取对象: 调用
localPool.claim()
尝试从池中获取可重用对象 - 创建新对象: 如果池中无可用对象,则调用
localPool.newHandle()
判断是否应该创建新对象- 根据回收比率(RATIO)控制新对象创建频率
- 如果应该创建,则调用抽象方法
newObject(handle)
创建对象
- 返回对象: 返回获取或创建的对象
✅ 3. 回收比率控制
LocalPool.newHandle()
方法实现回收比率控制:
DefaultHandle<T> newHandle() {if (++ratioCounter >= ratioInterval) {ratioCounter = 0;return new DefaultHandle<T>(this);}return null;
}
ratioCounter
计数器递增- 当达到
ratioInterval
(RATIO值) 时,重置计数器并创建新句柄 - 否则返回null,强制调用
newObject((Handle<T>) NOOP_HANDLE)
创建不参与回收的对象
✅ 4. 关键抽象方法:newObject(Handle<T> handle)
Recycler
是抽象类,用户需重写 newObject
定义对象的创建逻辑,例如
// 自定义可复用对象的Recycler
private static final Recycler<MyObject> RECYCLER = new Recycler<MyObject>() {@Overrideprotected MyObject newObject(Handle<MyObject> handle) {// 创建新对象时绑定Handle(后续通过Handle回收)return new MyObject(handle);}
};
核心作用
- 调度中枢:统一管理对象的 “获取 - 复用 - 创建” 流程,屏蔽底层复杂逻辑;
- 线程隔离:通过
FastThreadLocal
为每个线程分配独立LocalPool
,避免跨线程竞争;- 资源清理:
threadLocal
的onRemoval
方法在线程销毁时清理缓存,防止内存泄漏。
这种设计可以防止在突发流量时对象池无限增长,同时又能逐步适应负载。
3.7 对象回收模块
Handle.recycle()
和LocalPool.release()
实现对象回收
✅ 1. 核心接口
public interface Handle<T> extends ObjectPool.Handle<T> { }@UnstableApi
public abstract static class EnhancedHandle<T> implements Handle<T> {public abstract void unguardedRecycle(Object object);
}private static final class DefaultHandle<T> extends EnhancedHandle<T> {@Overridepublic void recycle(Object object) {if (object != value) {throw new IllegalArgumentException("object does not belong to handle");}localPool.release(this, true);}@Overridepublic void unguardedRecycle(Object object) {if (object != value) {throw new IllegalArgumentException("object does not belong to handle");}localPool.release(this, false);}
}
回收流程:
- 用户调用
handle.recycle(object)
方法回收对象 - 验证对象是否属于该句柄(防止误用)
- 调用
localPool.release(this, true)
进行回收
✅ 2. LocalPool.release() 方法
void release(DefaultHandle<T> handle, boolean guarded) {if (guarded) {handle.toAvailable();} else {handle.unguardedToAvailable();}Thread owner = this.owner;if (owner != null && Thread.currentThread() == owner && batch.size() < chunkSize) {accept(handle);} else if (owner != null && isTerminated(owner)) {this.owner = null;pooledHandles = null;} else {MessagePassingQueue<DefaultHandle<T>> handles = pooledHandles;if (handles != null) {handles.relaxedOffer(handle);}}
}
回收策略
- 状态更新: 将句柄状态从
STATE_CLAIMED
改为STATE_AVAILABLE
- 快速路径: 如果当前线程就是池的所有者线程,且批处理队列未满,则直接加入批处理队列
- 线程终止检查: 如果所有者线程已终止,则清理资源
- 慢速路径: 否则将对象放入MPSC队列供其他线程使用
批处理优化:
- 使用
ArrayDeque<DefaultHandle<T>> batch
作为批处理缓冲区 - 当同一线程频繁回收对象时,先放入批处理队列,减少对MPSC队列的操作
- 批处理大小由
chunkSize
控制(默认32)