Node.js 服务在 CentOS 上内存持续上涨并最终 OOM,通常要同时看 V8、CentOS 内存策略和应用代码。先分清是堆、rss 还是 external 在涨,再决定扩堆、调 Swap,还是直接查泄漏。
先看清 Node.js 在 CentOS 上到底用的是什么内存
排查时先区分 3 类内存:

- 堆内存:JavaScript 对象、闭包,最常见上涨点。
- 栈内存:基本类型、调用信息,生命周期通常较短。
- 原生内存:C++ 层使用,如
Buffer,不等同于 V8 堆。
V8 堆又分为:
- 新生代:短生命周期对象,使用 Scavenge。
- 老生代:长生命周期或晋升对象,通常由 Mark-Sweep、Mark-Compact 处理。
因此内存一直涨时,先判断增长发生在堆内,还是 rss 这类更大的常驻内存范围。
CentOS 系统层面,先把监控和内核参数调到可判断状态
先确认物理内存、Swap 和进程占用
free -h:看物理内存和 Swap 总量、已用量。top或htop:按%MEM找高占用进程。vmstat 1:持续观察虚拟内存和系统活动。/proc/meminfo:查看更细项。
先判断是 Node.js 进程失控,还是系统已频繁使用 Swap。

按机器情况调整 Swap
物理内存充足且业务对延迟敏感时,可减少甚至关闭 Swap;内存经常不够时,应补充 Swap。示例:创建 2GB 交换文件。

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab # 永久生效
把 swappiness 调低,减少主动换出
vm.swappiness 默认值是 30。对 Node.js 常驻服务,可按机器情况调低到 10:
sudo sysctl vm.swappiness=10
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf # 永久生效
必要时限制过度分配
vm.overcommit_memory:
0:默认策略1:总是允许2:严格限制
担心单进程申请过多内存时,可考虑设为 2,更早暴露问题。
回到 Node.js 进程本身,重点处理堆上限、泄漏点和数据处理方式
按业务需要调整堆内存上限
V8 在 64 位系统上的默认堆上限大约是 1.4GB,可用 --max-old-space-size 调整,单位 MB:
node --max-old-space-size=2048 app.js # 设置为2GB
PM2 可设置内存阈值自动重启:
module.exports = {
apps: [{
name: 'app',
script: 'app.js',
max_memory_restart: '2G' // 内存超过2GB时自动重启
}]
};
调大上限只是增加缓冲,不等于解决泄漏。
优先排查几类高发内存泄漏
- 全局变量:如忘记写
var/let/const,或挂到global。 - 未清理的定时器和监听器:
setInterval、setTimeout、EventEmitter。 - 闭包长期持有大对象。
- 外部资源未释放:如
fs.close、db.connection.end。
大文件和大结果集尽量改成流式处理
const fs = require('fs');
const readStream = fs.createReadStream('large-file.txt');
const writeStream = fs.createWriteStream('output.txt');
readStream.pipe(writeStream); // 流式传输,减少内存占用
缓存要限额,能外置就外置
进程内缓存要限制大小;多实例或数据量较大时,更适合 Redis、Memcached 这类进程外缓存,避免多个 Node.js 进程各自堆积副本。
内存还在涨时,用这些工具把问题定位到对象和代码层
先用 process.memoryUsage() 看增长发生在哪
rss:常驻内存heapTotal:堆总内存heapUsed:堆已用内存external:原生内存
setInterval(() => {
console.log(process.memoryUsage());
}, 5000);
用 Chrome DevTools 分析堆快照
使用 --inspect 启动后,在 Chrome 打开 chrome://inspect,到 Memory 面板对比多个时间点的堆快照,找出持续存活对象和未断开的引用链。
用 heapdump 导出现场
const heapdump = require('heapdump');
heapdump.writeSnapshot('/tmp/heapdump.heapsnapshot', (err, filename) => {
console.log('堆快照已保存到', filename);
});
用 memwatch-next 监控异常趋势
const memwatch = require('memwatch-next');
memwatch.on('leak', (info) => {
console.log('内存泄漏检测到:', info);
});
实操时怎么判断该先调系统还是先查代码
- 先用
free -h、top、vmstat 1确认是否系统整体内存紧张。 - 再看
process.memoryUsage(),判断增长集中在heapUsed、rss还是external。 - 如果只是业务峰值触顶,可调整
--max-old-space-size或 PM2 的max_memory_restart。 - 如果内存长期单向上涨,优先检查全局变量、监听器、闭包、连接和缓存。
- 仍无法定位,再用 DevTools、
heapdump、memwatch-next做对象级分析。
有效治理通常要同时做到:系统参数有边界、进程配置有上限、代码对象能释放、问题现场能留证。







