关于 openclaw-test-heap-leaks
用于诊断 Vitest 测试中的内存增长,并验证运行时泄漏修复的技能。它结合性能分析、堆证据和定向验证,区分模块图保留与应用对象泄漏。
主要功能
- 01按原测试配置复现内存增长
- 02收集 CPU、堆分析与快照证据
- 03区分模块保留和运行时泄漏
- 04按进程比较堆快照
- 05用专用测试工具验证运行时修复
适用场景
- 01调查 Vitest 工作进程内存上升
- 02通过堆快照追踪保留对象
- 03检查闭包泄漏修复
- 04选择测试路由或清理路径调整
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ openclaw/ openclaw --skill openclaw-test-heap-leaks安装后在 Claude Code 中输入/openclaw-test-heap-leaks确认已加载。
附带文件agents/openai.yamlscripts/heapsnapshot-delta.mjs
详细介绍
OpenClaw 测试堆内存泄漏
将此技能用于测试内存调查。如果有堆快照,不要仅凭 RSS 猜测。将快照名称的差异视为初步排查证据,而非结论;只有保留者或支配对象分析支持判断后,才能下结论。
先阅读 ../openclaw-test-performance/SKILL.md,了解当前的测试性能命令和证据判定方式。
对于运行时修复(例如网关等长期运行的服务中的闭包泄漏),请参阅下方的验证运行时修复——该方法使用专用测试框架,而非单元测试性能分析工作流。
工作流
-
首先复现出现问题的场景。
-
匹配实际入口和工作进程预算。要获取涵盖范围较广的 unit-fast 基线,以及每种配置的最大 RSS 和耗时最高的文件,请从以下命令开始:
pnpm test:perf:groups \ --config test/vitest/vitest.unit-fast.config.ts \ --allow-failures \ --output .artifacts/test-perf/unit-fast-memory.json -
对于怀疑有问题的文件,使用一个工作进程重新运行该文件,并收集耗时和 RSS 证据:
/usr/bin/time -l pnpm test <file> --maxWorkers=1 --reporter=verbose。 -
当前的
pnpm test执行计划由scripts/test-projects.mts制定。记录打印出的 Vitest 配置或分片,并在报告特定于配置或工作进程预算时保留该运行形态。
-
-
收集最有力的堆内存证据。
- 运行
pnpm test:perf:profile:runner -- --output-dir .artifacts/test-perf/vitest-runner-profile -- <file>,为单元测试运行器生成 CPU 性能分析和采样堆性能分析。在 DevTools 中打开堆性能分析文件,并检查占用最大的分配类型。 - 采样生成的
.heapprofile不是.heapsnapshot;不要将其传给快照差异辅助脚本。 - 如果专用测试框架或其他已知生成器会输出多个
.heapsnapshot文件,请使用.agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs比较同一 PID 的快照。 - 如果没有可用的快照或堆性能分析文件,RSS 增长可作为初步排查证据,但泄漏分类仍应保持为未定。
- 运行
-
先对增长进行分类,再选择修复方案。
- 如果增长主要来自 Vite/Vitest 转换后的源码字符串、
Module、system / Context、字节码、描述符数组或属性映射,则将其视为长期运行的工作进程中模块图可能被保留而持续增长。 - 如果增长主要来自应用对象、缓存、缓冲区、服务句柄、计时器、模拟状态、sqlite 状态或类似运行时对象,则将其视为可能存在清理或生命周期泄漏。
- 如果名称含义不明确,不要轻率下结论;请在 DevTools 中检查主要差异项的保留者或支配对象。
- 如果增长主要来自 Vite/Vitest 转换后的源码字符串、
-
修复正确的层面。
- 对于共享工作进程中可能由保留转换后模块导致的增长:
- 检查所属的 Vitest 配置和
scripts/test-projects.test-support.mjs中的进程分块。只有在相同运行形态的证据表明原因是共享工作进程保留模块时,才在此处调整进程生命周期或项目归属。 test/vitest/vitest.unit-fast-isolated.config.ts用于经审查、需要全新模块图的有状态测试。不要将其用作通用的内存热点列表。- 对于实际泄漏:
- 修复涉及的测试或运行时清理路径。
- 检查是否缺少
afterEach/afterAll、模块重置不完整、全局状态被保留、DB 句柄未释放,或监听器/计时器在文件运行结束后仍然存在。
-
使用最直接的证据进行验证。
- 重新运行相同的分组或限定范围命令,确认最大 RSS 趋势或 OOM 已改善。
- 如果专用生成器会输出堆性能分析文件或快照,请重复运行同一生成器并比较等效产物。
- 对于路由或配置变更,验证预期的 Vitest 配置或分片已启动,且受影响的测试已完成。
启发式判断
- 不要把所有情况都称为泄漏。在非隔离的共享 Vitest 项目中,增长可能是 worker 生命周期问题,而不是应用对象泄漏。
scripts/test-projects.mts、scripts/test-group-report.mts和scripts/run-vitest-profile.mts分别是当前的执行、分组 RSS 和性能分析入口。[test] starting ...行用于确定要复现的 Vitest 配置或分片。.artifacts/vitest-shard-timings.json存储用于调度的配置/分片耗时。它不是文件级内存热点或行为清单。- 如果同一 worker PID 中的相同保留对象类型在多个时间间隔内持续增长,应以快照为准,而不是凭直觉判断;然后通过保留者证据确认有疑问的调用。
快照对比
- 直接对比:
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs before.heapsnapshot after.heapsnapshot
- 在单个 lane 中按 PID 自动选择最早和最晚的快照:
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs --lane-dir <snapshot-directory>
- 常用标志:
--top 40--min-kb 32--pid 16133
先查看增幅最大的正向差值。模块转换产物大幅增长,说明问题可能与共享进程生命周期或项目所有权有关;运行时对象大幅增长,则可能是真正的泄漏。如果仅凭名称无法确定原因,请先在 DevTools 中打开同一组快照,并检查排名靠前条目的保留者/支配者,再判定根因。
验证运行时修复(非测试内存)
上述工作流用于诊断 Vitest worker 内存增长。若要验证运行时/闭包修复确实释放了捕获的状态,请使用专用测试工具:
pnpm leak:embedded-run— 运行scripts/embedded-run-abort-leak.ts。在模拟runEmbeddedAttempt的函数作用域中循环执行 N 次中止的运行,写入堆快照,并使用FinalizationRegistry跟踪实例计数,同时结合 RSS 差值,报告保留增长的 PASS/FAIL 结论。
模式:
closure-extracted(默认)— 生产修复形态(辅助函数位于模块作用域)。closure-inline— 修复前的形态(闭包位于 runner 作用域内)。将其用作灵敏度检查:如果它通过,说明你破坏了测试工具,而不是修复了 bug。synthetic-leak— 通过模块级 bucket 故意保留对象。先用它确认测试工具能检测到泄漏,再相信真实修复的 PASS 结果。
快照保存在 .tmp/embedded-run-abort-leak/ 中。使用与上述相同的脚本进行差异对比:
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs \
.tmp/embedded-run-abort-leak/baseline-*.heapsnapshot \
.tmp/embedded-run-abort-leak/batch-N-*.heapsnapshot --top 30
修复其他运行时泄漏时,应在此测试工具旁添加新的测试工具,而不是改造现有工具。测试夹具函数应模拟发生泄漏的函数的词法作用域,而不是使用通用的中止循环。
输出要求
使用此技能时,请报告:
- 精确的复现命令。
- 复现了哪个 Vitest 配置或目标,以及有快照时对比了哪个 PID。
- 堆分析或快照差值中占主导地位的保留对象类型(如有)。
- 问题更可能是真正的泄漏,还是共享 worker 中保留的模块增长,以及保留者/支配者检查是否证实了这一判断。
- 具体的修复或降低影响的补丁。
- 你验证了哪些内容,以及哪些内容因快照开销而无法验证。