夜雨聆风学习资料网

ARTICLE · 1286389

hvigor 内存高水位GC控制插件

hvigor 内存高水位GC控制插件
全部方案,代码和A/B测试验证全部由 DeepSeek-V4.1-Flash-1017完成,我没有写一行代码。DS-Flash 我愿称之为又快又好又便宜。
我先大致讲一下原理。假设一个水库的满水位是100,由于天天下雨这时水位线已经到了90,这个时候水位已经比较高了我们要提前放一些水(GC),否则水位不断增长很可能溃坝(峰值太高 OOM)。要随时动态调整(GC)保持一个稳定的水位。
如果你能看懂下面的图立马就能明白高水位GC降峰值
那么我们怎么找到这个高水位点呢???
黑盒上我们可以先跑几次全量编译,采集 heapused 大体就能得到峰值内存和高水位点。在高水位点附近我们要开始GC并动态监控。
结合代码白盒分析可知内存占用大户主要由2阶段组成,一是 buildStart 开始的 tsc-checker 会解析所有文件的TS AST到内存中,buildStart 结束之后这时水位已经比较高了,紧接着进入build阶段,rollup transform 钩子会对每个文件进行UI语法转换,这是第2块比较耗内存的地方,且每个文件都要进行转换,内存在高水位又开始逐步增长,且文件越多内存增长的越多。
我找的高水位点就是buildStart结束,build开始这个点,在 mouduleParsed 钩子中进行检测和动态GC。 mouduleParsed 对于每个文件都会调用1次(无论全量还是增量),主要解决rollp transform 阶段的内存增量过高问题。
前提需要配置 exposeGC 使能 GC。
"nodeOptions": {  "exposeGC": true}
DS搞的算法策略:
插件加载(每次构建)  └─ 读配置:env > gc-watermark.config.json > 内置默认       ↓moduleParsed(每个模块一次,含缓存模块)  ├─ n++  ├─ mode=off ? 只采样(traceOut 非空时)→ return  ├─ 未标定?  │    ├─ global.gc()                    ← 标定 GC  │    ├─ floor = heapUsed  │    ├─ margin = marginMB>0 ? marginMB : clamp(floor × marginPct%)  │    ├─ limit = floor + margin  │    └─ calibrated = true  ├─ n % checkEvery == 0 ?  │    ├─ heap = process.memoryUsage().heapUsed  │    ├─ heap <= limit ? return  │    ├─ global.gc()                    ← 强制 GC  │    ├─ after = heapUsed  │    ├─ 若 gcCount>1 且 n−lastGcN < minModules:margin ×1.5(夹取)  │    ├─ limit = after + margin  │    ├─ 若 limit <= GC 前堆:limit = GC 前堆 + 128MB   ← 防连击  │    └─ lastGcN = n  └─ traceOut 非空:每 checkEvery 个模块记一条 {n, heap}       ↓buildEnd  └─ log 摘要;traceOut 非空时写 JSON(samples + gcs + 统计)
配置:
 * config 字段: *   mode        "adaptive" | "off" *   marginPct   水位余量 = 存活集 × pct%(默认 12;下限 64MB、上限 max(1.5GB, 25%×存活集)) *   marginMB    手动固定余量 MB(>0 覆盖 marginPct) *   checkEvery  每 N 个模块检查一次(默认 8) *   minModules  两次 GC 间隔 < N 模块时余量 ×1.5(默认 1500) *   log         是否打印标定/GC 日志 *   traceOut    轨迹输出路径(A/B 用;空字符串 = 不写) */
策略的数学特征:
峰值 ≈ live_end + margin(+ 少量过冲)GC 次数 ≈ transform 期间总垃圾量 / margin单次 GC 成本 ∝ live(标记存活对象)比例不变性:margin 按 live 比例取,所以同一 pct 在小/大工程上保持相同的相对峰值,GC 次数也大致同量级——这是它能跨工程的原因。不是绝对上限:live 涨,limit 跟着涨;它只削“存活集之上的垃圾”。只有 baseline 峰值 > live + margin 时才有效;如果 baseline 本身就很紧,pct=12可能完全不触发(无收益也无害)。
调参速查:
启用  mode:"adaptive"默认平衡  marginPct:12大模块导致过冲  checkEvery:4或1频繁 GC / 构建变慢  调大 minModules 或 marginPct;看 log:true固定实验值  marginMB: N + minModules:0跑 baseline mode:"off"抓轨迹  traceOut:"/abs/path/trace.json"
DS预计峰值26G,pct 12大约能够降低 2G 峰值。如果通过这个GC水位外挂插件能够降低1-2G的峰值内存还是值得的。
只测试过一个小工程,实际效果需要针对你的工程实测并调参。你要做的就是让AI辅助针对你的工程调出来一个适当的参数。
仓库地址:https://github.com/lesliebeijing/gcwatermarkplugin

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料