一、技能概述
该技能用于调查 pnpm test 运行时的内存增长、Vitest OOM、RSS 峰值以及堆快照差异。它通过系统化的取证工作流,避免仅凭 RSS 数值进行猜测。
核心原则:将快照名称差异视为分类证据而非结论,直到 retainers(引用链)或 dominators(支配树)分析支持该判断。
二、核心功能
1. 测试内存取证工作流(Test Memory Forensics)
技能提供了一套完整的内存问题复现与诊断流程:
复现问题:
pnpm canvas:a2ui:bundle && \
OPENCLAW_TEST_MEMORY_TRACE=1 \
OPENCLAW_TEST_HEAPSNAPSHOT_INTERVAL_MS=60000 \
OPENCLAW_TEST_HEAPSNAPSHOT_DIR=.tmp/heapsnap \
OPENCLAW_TEST_WORKERS=2 \
OPENCLAW_TEST_MAX_OLD_SPACE_SIZE_MB=6144 \
pnpm test
-
保持
OPENCLAW_TEST_MEMORY_TRACE=1启用,使 wrapper 在生成快照的同时打印每个文件的 RSS 摘要 -
如果报告涉及特定的 shard 或 worker 预算,保留该形状进行复现
识别真实 lane 名称:
-
从
[test-parallel] start ...行或pnpm test --plan中获取真实的 lane 名称 -
不要假设只有一个
unit-fastlane——本地计划通常会拆分为unit-fast-batch-*等多个 lane
等待多次快照:
-
至少从同一个 lane 采集两次间隔快照
-
比较同一个 PID 在真实 lane 目录(如
.tmp/heapsnap/unit-fast-batch-2/)内的快照
2. 堆快照对比工具
技能提供了专用的对比脚本:
直接比较两个文件:
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs before.heapsnapshot after.heapsnapshot
自动选择每个 PID 的最早/最新快照:
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs --lane-dir .tmp/heapsnap/unit-fast-batch-2
常用参数:
-
--top 40—— 显示前 40 个差异最大的对象 -
--min-kb 32—— 忽略小于 32KB 的差异 -
--pid 16133—— 指定特定 PID 进行分析
分析优先级:先读取正向增长最大的条目——模块转换产物的巨大正向增长指向 lane 隔离问题;运行时对象的巨大正向增长指向真实泄漏。如果名称本身不足以判断,在 DevTools 中打开同一对快照,检查 retainers/dominators 后再声明根因。
3. 内存增长分类
技能提供了清晰的分类框架:
| 增长类型 | 特征 | 处理方式 |
|---|---|---|
| 保留模块图增长 | 被 Vite/Vitest 转换的源字符串、Module、system/Context、bytecode、descriptor arrays、property maps 主导 | 通常是长期存活 worker 中的模块图保留问题 |
| 真实泄漏 | 被应用对象、缓存、buffers、server handles、timers、mock state、sqlite state 等运行时对象主导 | 查找缺失的 afterEach/afterAll、模块重置缺口、残留的全局状态、未释放的 DB handles 或存活过文件的 listeners/timers |
如果名称模棱两可,停止做出自信的判断,在 DevTools 中检查 retainers/dominators。
4. 修复策略
对于保留模块图增长:
-
优先通过 timing 和 hotspot 驱动的调度修复
-
检查文件是否已在
test/fixtures/test-timings.unit.json中体现 -
仅在 timing-driven peeling 不足时,更新
test/fixtures/test-parallel.behavior.json -
对于单独运行安全但会膨胀共享 worker 堆的文件,优先使用
singletonIsolated -
如果文件本应已被 timings 剥离但不在
test/fixtures/test-timings.unit.json中,明确指出
对于真实泄漏:
-
修补涉及的测试或运行时清理路径
-
查找缺失的
afterEach/afterAll、模块重置缺口、残留的全局状态、未释放的 DB handles 或存活过文件的 listeners/timers -
用最直接的证据验证修复
5. 运行时修复验证(Runtime Fixes Validation)
上述工作流用于诊断 Vitest worker 内存增长。对于验证运行时/闭包修复是否真正释放了捕获的状态,使用专用测试工具:
pnpm leak:embedded-run
该工具运行 scripts/embedded-run-abort-leak.ts,在模拟 runEmbeddedAttempt 的函数作用域中循环执行 N 次 abort 运行,写入堆快照,并通过 FinalizationRegistry 进行跟踪实例计数 + RSS delta 报告 PASS/FAIL 结论。
三种模式:
-
closure-extracted(默认)—— 生产修复形态(模块作用域的 helper) -
closure-inline—— 修复前形态(runner 作用域内的闭包),用作敏感性检查 -
synthetic-leak—— 通过模块级 bucket 故意保留,用于确认 harness 能检测泄漏
快照落在 .tmp/embedded-run-abort-leak/,使用同样的脚本进行对比。
三、主要用途
-
诊断 Vitest OOM 和 RSS 峰值
当pnpm test因内存不足失败或 RSS 持续增长时,系统化地定位根因 -
区分真实泄漏与模块图保留
通过堆快照对比和分类框架,避免将 worker-lifetime 问题误判为应用程序对象泄漏 -
验证运行时闭包修复
使用pnpm leak:embedded-run专用 harness 验证闭包/运行时修复是否真正释放了捕获状态 -
优化测试调度
通过识别 hotspot 文件和更新 timings manifest,将内存密集型文件移出共享 worker lane -
PR 审查中的内存回归证明
在涉及内存敏感变更的 PR 中,提供快照对比证据支撑审查决策
四、重要原则
-
不凭 RSS 数值下结论:堆快照可用时,不要仅凭 RSS 猜测
-
快照名称差异是分类证据,不是结论:需要 retainers/dominators 支持才能下定论
-
不要将所有增长都称为泄漏:
unit-fast或unit-fast-batch-*的大幅增长可能是 worker-lifetime 问题,而非应用程序对象泄漏 -
信任快照胜过直觉:当同一个 worker PID 中相同保留对象家族跨多个间隔增长时,信任快照,然后用 retainers 证据确认模糊的判断
-
先检查真实 lane 名称:不要假设单一的
unit-fastlane -
等待至少两次间隔快照:再下结论
-
运行时修复使用专用 harness:不要用测试内存取证工作流验证运行时闭包修复
五、输出要求
使用该技能后,报告必须包含:
-
精确的复现命令
-
比较了哪个 lane 和 PID
-
快照差异中主导的保留对象家族
-
问题是真实泄漏还是共享 worker 保留模块增长,以及 retainers/dominators 是否确认了该判断
-
具体的修复或影响削减补丁
-
验证了什么,以及快照开销阻止了哪些验证
总结:OpenClaw Test Heap Leaks 是 OpenClaw 项目中用于诊断和修复测试内存泄漏的专业技能。它通过堆快照对比、RSS 趋势分析和专用运行时验证工具,帮助区分“真实应用对象泄漏”与“Vite/Vitest 转换模块保留”两类不同性质的内存问题,并提供针对性的修复策略。该技能适用于 OpenClaw 贡献者和维护者在测试 OOM 排查、内存回归预防和运行时闭包泄漏验证的场景。