尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SkyWalking Java Agent 线程池场景内存泄漏排查:Worker 线程增强与跨线程追踪修复指南

SkyWalking Java Agent 线程池场景内存泄漏排查:Worker 线程增强与跨线程追踪修复指南 可观测性后端微服务云原生【免费下载链接】skywalkingAPM, Application Performance Monitoring System项目地址https://gitcode.com/gh_mirrors/sky/skywalking点击查看免费下载本文基于 Memory-leak-enhance-Worker-thread.md 展开结合仓库内的 Trace 数据协议与上下文传播协议文档进行源码级佐证。文章讲解在使用线程池时TraceSegment数据无法上报、内存无法回收内存泄漏的典型故障其根因是 SkyWalking Java Agent 对 Worker 线程的增强机制与跨线程追踪设计并给出「使用受支持线程调度框架」与「自定义线程池 RunnableWrapper包装任务」两种可直接落地的修复方案帮助读者在 SpringAsync、自定义ExecutorService等场景下正确完成跨线程追踪。问题现象线程池中的 TraceSegment 无法上报内存无法回收官方 FAQ 给出了这个问题的标准描述When using a thread pool,TraceSegmentdata in a thread cannot be reported and there are memory data that cannot be recycled (memory leaks).翻译过来即在使用线程池时某个线程中的TraceSegment数据无法上报且存在无法被回收的内存数据即内存泄漏。要理解这个问题首先需要明确TraceSegment在 SkyWalking 中的定位。根据 trace-data-protocol-v3.md 中的定义Segment 是 SkyWalking 中的独特概念它应当包含单个 OS 进程内一次请求的全部 Span通常对应基于语言单线程的一次执行。上报时Agent 通过SegmentObject消息包含traceId、traceSegmentId、spans、service、serviceInstance等字段将整段数据一次性发给后端。后端通过TraceSegmentReportService的 gRPC 流式接口接收这些段数据仓库中的 GRPCNoServerTest.java 就演示了TraceSegmentReportServiceGrpc建立上报通道、collect(StreamObserverSegmentObject)发送段数据的调用方式。由此可见TraceSegment是 Agent 侧一次追踪的完整数据载体它必须被完整地构建、最终上报并释放。一旦某个执行单元这里是线程池中的任务线程上的追踪上下文没有正确建立该段数据既无法形成完整链路上报给 OAP又会滞留在内存中无法被回收最终表现为数据缺失 内存泄漏的双重故障。复现示例在 ThreadFactory 中包装 Runnable 的错误姿势FAQ 给出了一个会触发该问题的典型写法使用 Spring 的ThreadPoolTaskExecutor并自定义线程工厂ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor(); executor.setThreadFactory(r - new Thread(RunnableWrapper.of(r)));这段代码的意图是让线程池创建的每个线程都携带追踪能力因此在ThreadFactory.newThread(Runnable r)里对传入的r套了一层RunnableWrapper.of(r)。但从结果上看它正是内存泄漏与数据无法上报的诱因。其关键问题在于ThreadFactory的newThread(Runnable r)只在创建 Worker 线程时被调用它拿到的是该线程的首任务或初始任务而不是后续每次提交进来的任务追踪上下文的捕获发生在RunnableWrapper.of(...)被调用的瞬间。在ThreadFactory中包装等于在线程创建时刻捕获上下文而真实的业务任务往往是在之后的不同线程上下文中提交的因此后续提交任务的追踪上下文无法传递到执行线程跨线程链路断裂段数据无法正确完成与上报。结论包装的位置错了。上下文必须在任务提交点捕获并传递而不是在线程创建点。根因分析跨线程追踪为什么必须增强任务线程FAQ 原文从设计层面对根因做了两点总结Worker 线程在使用线程池时会被增强Worker threads are enhanced when using the thread pool基于 SkyWalking Java Agent 的设计跨线程追踪时必须增强任务线程when tracing a cross thread, you must enhance the task thread。结合仓库协议文档可以进一步理解第二点背后的数据模型在 trace-data-protocol-v3.md 中SegmentReference消息专门用于表达两个已有 Segment 之间的关联并通过RefType区分两种引用类型RefType含义说明CrossProcess跨进程引用指向另一个 OS 进程中的父 Segment对应 RPC 场景CrossThread跨线程引用指向同一进程内、另一个线程中的父 Segment仅用于具有线程概念的语言当一次追踪从提交线程跳到线程池的执行线程时子段需要通过refs携带指向父段提交线程中的段的CrossThread引用字段说明中明确写着refType CrossThread时父段与当前段属于同一服务、同一实例。也就是说跨线程不是断链而是通过SegmentReferenceCrossThread 类型把执行线程中的新段挂到提交线程的段上形成完整链路要做到这一点执行任务的线程必须被增强——它需要能感知上下文、创建携带引用关系的新段并在任务结束后把段数据上报、释放。同样在跨进程场景Agent 通过sw8传播头见 x-process-propagation-headers-v3.md传递traceId / parentTraceSegmentId / parentSpanId等 8 个字段而跨线程场景则在进程内完成类似的信息传递。此外可选的关联上下文协议见 x-process-correlation-headers-v1.md也要求当追踪上下文跨线程、跨进程传播时关联上下文应一并传播。这些设计共同决定了任务线程必须被正确增强并携带上下文否则执行线程上产生的段缺少父子引用、无法归并到原链路数据滞留在 Agent 内存中形成泄漏。解决方案一使用受支持的线程调度框架零改造如果业务本身运行在 SkyWalking Java Agent 官方支持的线程调度框架Thread Schedule Framework之上则无需任何代码修改即可完成跨线程追踪。FAQ 给出的指引是查阅 SkyWalking Java Agent 支持的框架清单Supported-list其中典型的例子是Spring Framework 的Async——Agent 的相应插件已经处理了任务提交与执行的上下文传递开发者只需要正常使用框架能力即可。因此如果你的代码满足以下条件优先走这条路使用的是受支持的调度框架如 SpringAsync、以及清单中列出的其他线程调度组件不需要自定义线程工厂、自定义包装逻辑希望最小化侵入、避免自行包装带来的上下文处理错误。此时应移除上文中在ThreadFactory里包装RunnableWrapper的自定义逻辑恢复框架默认行为让插件负责跨线程传播。解决方案二自定义线程池场景使用 RunnableWrapper 包装提交任务如果使用的是自定义线程池Custom Thread Pool则必须手动增强任务线程。FAQ 给出的标准做法是在任务提交时用RunnableWrapper.of(task)包装任务而不是在线程工厂里包装。FAQ 原文示例Executors.newFixedThreadPool场景ExecutorService executorService Executors.newFixedThreadPool(1); executorService.execute(RunnableWrapper.of(new Runnable() { Override public void run() { //your code } }));为了让该示例真正可运行、可维护下面补充为完整形态含导入、异常处理与优雅关闭import org.apache.skywalking.apm.toolkit.trace.RunnableWrapper; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class CrossThreadTraceDemo { public static void main(String[] args) throws InterruptedException { ExecutorService executorService Executors.newFixedThreadPool(1); // 在任务提交点包装 Runnable // RunnableWrapper.of() 会捕获提交线程当前正在追踪的上下文 // 并在执行线程运行 run() 前将其恢复从而实现跨线程上下文传递。 executorService.execute(RunnableWrapper.of(new Runnable() { Override public void run() { // 你的业务逻辑此处运行在线程池的 Worker 线程上 // 但由于任务已被增强wrapped该线程产生的 Span 会 // 通过 CrossThread 引用关联到提交线程的 Segment 上。 } })); executorService.shutdown(); executorService.awaitTermination(10, TimeUnit.SECONDS); } }要点解读包装时机 提交时机RunnableWrapper.of()应出现在execute()/submit()调用的参数位置即任务进入队列的那一刻此时提交线程的追踪上下文处于活状态可以被捕获不要在线程工厂中包装如本文复现示例所述ThreadFactory中的包装捕获的是线程创建时刻的上下文无法覆盖后续每个任务的真实上下文更多用例FAQ 明确指向跨线程解决方案 API文档across thread solution APIs位于 apache/skywalking-java 仓库的Application-toolkit-trace-cross-thread.md其中包含Runnable、Callable等多形态任务的包装用法。注意该 Toolkit 位于 Java Agent 仓库skywalking-java与当前 OAP 后端仓库skywalking属于同一生态的不同组件集成时需确保 Agent 与 Toolkit 版本配套。两种方案对比与选型建议维度线程调度框架如 Spring Async自定义线程池 RunnableWrapper代码改造量零改造需在提交点包装任务适用场景框架能力覆盖且受 Agent 支持清单认可自有线程池、框架插件未覆盖的场景上下文传递由 Agent 插件自动完成由RunnableWrapper.of()在提交点捕获、执行点恢复出错风险低中需避免在 ThreadFactory 等错误位置包装验证修复效果完成修复后建议从两个维度验证链路完整性在 UI 上查看该线程池任务对应的 Trace确认执行线程产生的 Span 已通过 CrossThread 引用挂接到提交线程的 Segment 上形成完整链路可结合 trace-data-protocol-v3.md 中SegmentReference/RefType的语义理解 UI 上的连线关系内存回收对应用做持续压力运行并观察堆内存曲线确认不再出现随时间线性增长的滞留数据同时确认后端 OAP 能持续收到该服务上报的 Segment后端上报入口参见 TraceSegmentReportServiceHandler.java。关联资料本文档Memory-leak-enhance-Worker-thread.mdFAQ 索引docs/en/FAQ/README.mdRuntime 分类下列出了本问题Trace 数据协议Segment / SegmentReference / RefType 定义trace-data-protocol-v3.md跨进程传播头协议 sw8x-process-propagation-headers-v3.md关联上下文传播协议 sw8-correlationx-process-correlation-headers-v1.md小结线程池场景下的TraceSegment 无法上报 内存泄漏本质是跨线程追踪上下文没有在正确的时机被增强与传递。牢记 SkyWalking Java Agent 的设计约束——跨线程必须增强任务线程优先选择 Agent 官方支持的线程调度框架如 SpringAsync零改造接入自定义线程池则务必在任务提交点使用RunnableWrapper.of(task)包装杜绝在ThreadFactory中包装的错误姿势。这样既能保证跨线程链路的完整性也能让段数据正常上报、及时回收从根本上消除内存泄漏。赞分享可观测性后端微服务云原生【免费下载链接】skywalkingAPM, Application Performance Monitoring System项目地址https://gitcode.com/gh_mirrors/sky/skywalking点击查看免费下载相关推荐SkyWalking Java Agent 线程池场景内存泄漏排查Worker 线程增强与跨线程 TraceSegment 上报SkyWalking Java Agent 线程池场景内存泄漏排查Worker 线程增强与跨线程 TraceSegment 上报 导读 本文围绕 SkyWal可观测性APM链路追踪指标监控日志分析微服务线程级内存追踪mimalloc如何让内存泄漏排查效率提升10倍线程级内存追踪mimalloc如何让内存泄漏排查效率提升10倍 你是否曾为多线程程序的内存泄漏问题头疼明明总内存占用异常增长却找不到具体是哪个线程在偷偷内存管理系统编程艾尔登法环存档编辑器3步解锁游戏无限可能告别重复刷图的烦恼艾尔登法环存档编辑器3步解锁游戏无限可能告别重复刷图的烦恼 还在为艾尔登法环中重复刷装备而烦恼吗想要快速体验不同build却不想从头开始ER Save桌面应用游戏开发上一篇偏微分方程可视化终极指南GitHub项目中的热传导方程三维动画模拟下一篇ntlm_theft vs 传统钓鱼为什么这款开源工具能绕过Windows Defender检测创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表