Node.js 内存泄漏排查记
上周值班,监控报警说内存占用超过 80%。看了一眼 Grafana,内存在过去几天里缓慢上升,典型的内存泄漏。
定位问题
首先想到的是最近的代码变更。查了 Git 记录,三天前有个同事改了缓存逻辑。
看了眼代码:
const cache = {};
function getData(key) {
if (!cache[key]) {
cache[key] = fetchData(key);
}
return cache[key];
}
好家伙,无限增长的缓存。key 是用户 ID,每来一个新用户就往里塞一条,永远不清理。
验证
先用 node --inspect 启动服务,然后用 Chrome DevTools 连上去,看一下堆内存快照。
果然,cache 对象越来越大,里面存了几十万个用户数据。
临时修复
加上过期时间:
const cache = new Map();
function getData(key) {
const item = cache.get(key);
if (item && Date.now() - item.time < 3600000) { // 1小时过期
return item.data;
}
const data = fetchData(key);
cache.set(key, { data, time: Date.now() });
return data;
}
然后加了个定时清理:
setInterval(() => {
const now = Date.now();
for (const [key, item] of cache.entries()) {
if (now - item.time > 3600000) {
cache.delete(key);
}
}
}, 600000); // 10分钟清理一次
上线后内存稳定了。
长期方案
临时方案虽然能用,但不够优雅。后来用 Redis 做了分布式缓存,内存问题彻底解决。
一些工具
排查 Node.js 内存泄漏,常用的工具有:
node --inspect+ Chrome DevTools:最直观heapdump:生成堆快照文件,离线分析clinic.js:一个诊断工具套件,推荐试试
npm install -g clinic
clinic doctor -- node app.js
它会生成一个可视化的报告,帮你定位问题。
经验总结
- 缓存一定要有过期策略,除非你确定它不会无限增长
- 内存监控很重要,提前发现问题比线上炸了再修强
- 代码 Review 的时候多注意全局变量和闭包
这次的问题其实挺低级的,但线上跑了好几天才爆出来。说明我们的监控和测试覆盖都还不够。