CentOS 上 Node.js 内存管理实用技巧
一 监控与诊断
- 使用 process.memoryUsage() 定期采样,关注 rss、heapTotal、heapUsed、external、arrayBuffers,便于发现异常增长趋势。示例:setInterval 每 5–10 秒打印一次。
- 启用 Chrome DevTools:以 node --inspect your-app.js 启动,在 chrome://inspect 进行堆快照、分配时间线分析。
- 生成堆转储定位泄漏:使用 heapdump 在关键时机写入快照,或在代码中按需调用 heapdump.writeSnapshot(‘/path/snap.heapsnapshot’);配合 DevTools 的 Comparison 视图比对对象增长。
- 事件监听:使用 node-memwatch 监听 ‘leak’ 事件,作为辅助告警手段。
- 必要时暴露 GC:启动时加 –expose-gc,在测试环境配合 global.gc() 验证回收效果(仅用于诊断,生产不建议)。
- 生产可观测性:用 prom-client 暴露 node_memory_usage_bytes 等指标,结合 Prometheus + Grafana 设置阈值告警与趋势面板。
二 设置与守护内存上限
- 直接启动参数:设置老生代上限(单位 MB),如 node --max-old-space-size=2048 app.js。
- 环境变量:在 shell 或 systemd 中设置 NODE_OPTIONS=“–max-old-space-size=1024”,再启动应用。
- 进程管理:使用 PM2 配置 max_memory_restart(如 ‘1G’),内存超限自动重启,适合兜底。
- Docker 场景:同时设置容器内存与 Node 堆上限,如 docker run -m 4g your-image node --max-old-space-size=2048 app.js,避免容器 OOM 先于 V8 回收。
- 经验值:堆上限不宜超过物理内存;在 64 位系统上,单进程通常设置在 8–16GB 区间,更高需充分压测与隔离。
三 代码与架构层面的优化
- 处理大对象与文件时优先使用 Streams,避免一次性读入内存;对大数据集采用 分页/游标 与 流式聚合。
- 全程使用 异步 I/O(Promise/async-await),避免 阻塞事件循环;将长任务拆分为小块,用 setImmediate()/process.nextTick() 让出控制权。
- 优化 数据库查询(索引、只查必要字段、连接池),减少单次请求的内存驻留时间。
- 合理使用 内存缓存(如 node-cache),设置 TTL 与 容量上限,避免缓存膨胀。
- 反向代理与静态资源:用 NGINX 处理静态文件、缓存与 负载均衡,WebSocket/SSL/TLS 也建议前置到 NGINX,降低 Node 进程压力。
四 快速排查与处置流程
- 基线采样:在稳定状态下采集 2–3 次堆快照作为基线;问题复现时再采集 2 次以上,用 Comparison 找出持续增长的对象构造器与保留路径。
- 强制 GC 验证:以 –expose-gc 启动,在关键路径前后调用 global.gc() 并对比 heapUsed;若回收后占用仍不下降,高概率为泄漏。
- 在线分析:用 –inspect 连接 DevTools Memory 面板,做 Heap Snapshot/Record Allocation,定位被意外持有的引用(闭包、全局缓存、未解绑事件监听等)。
- 兜底策略:在 PM2 或 systemd 中配置 内存超限重启,并结合 日志/指标 告警,防止故障扩散。