当前位置: 首页 > news >正文

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获取逻辑}
}

配置参数解析:

  1. maxCapacityPerThread: 每个线程池的最大容量,默认4K
  2. RATIO: 回收比例,控制对象回收频率,默认8(每8次尝试回收1次)
  3. chunkSize: 每个Link存储的对象数量,默认32
  4. BLOCKING_POOL: 是否使用阻塞模式,默认false
  5. 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. 工作原理

  1. 禁用检查: 如果 maxCapacityPerThread == 0,直接创建新对象并返回(不参与回收)
  2. 获取本地池: 通过 threadLocal.get() 获取当前线程的本地池
  3. 尝试获取对象: 调用 localPool.claim() 尝试从池中获取可重用对象
  4. 创建新对象: 如果池中无可用对象,则调用localPool.newHandle()判断是否应该创建新对象
    • 根据回收比率(RATIO)控制新对象创建频率
    • 如果应该创建,则调用抽象方法 newObject(handle) 创建对象
  5. 返回对象: 返回获取或创建的对象

✅ 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,避免跨线程竞争;
  • 资源清理threadLocalonRemoval 方法在线程销毁时清理缓存,防止内存泄漏。

这种设计可以防止在突发流量时对象池无限增长,同时又能逐步适应负载。

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)
http://www.dtcms.com/a/393831.html

相关文章:

  • 网页开发入门:CSS与JS基础及BS/CS架构解析
  • css单位换算及适配
  • Java制作双脑同步 Hemi-Sync 音频
  • webrtc弱网-ProbeBitrateEstimator类源码分析与算法原理
  • 在OpenHarmony上适配图形显示【4】——rk3568_4.0r_mesa3d适配
  • 嵌入式(3)——RTC实时时钟
  • 内核模块组成和裁剪参考表
  • 140-understanding_the_armv8.x_and_armv9.x_extensions_guide
  • 【序列晋升】40 Spring Data R2DBC 轻量异步架构下的数据访问最佳实践
  • TGRS | 视觉语言模型 | 语言感知领域泛化实现高光谱跨场景分类, 代码开源!
  • Oracle / MySQL / MariaDB / SQL Server 常用连接与基础查询(Linux操作系统上)
  • 将 Jupyter Notebook 转换为 PDF
  • torchvision 编译安装 nano
  • 华为昇腾 910 到 950 系列 NPU 深度解析
  • 设计模式---门面模式
  • SQL Server从入门到项目实践(超值版)读书笔记 26
  • Datawhale学习笔记——深度语义匹配模型DSSM详解、实战与FAQ
  • 一文了解瑞萨MCU常用的芯片封装类型
  • LeetCode:44.二叉搜索树中第K小的元素
  • 初学者如何系统性地学习Linux?
  • LeetCode:43.验证二叉搜索树
  • [学习log] OT/ICS工业控制系统渗透测试
  • 六边形箱图 (Hexbin Plot):使用 Matplotlib 处理大规模散点数据
  • LinuxC++项目开发日志——基于正倒排索引的boost搜索引擎(2——Parser解析html模块)
  • 电脑能ping开发板,开发板不能ping电脑的解决方法:
  • git 覆盖:检出特定分支的文件到当前分支
  • CentOS 8.5.2.111部署Zabbix6.0
  • 【Elasticsearch面试精讲 Day 20】集群监控与性能评估
  • hive调优系列-3.HQL语法和运行参数层面
  • 计算机网络学习(三、数据链路层)