引言
在开发和部署Node.js应用程序时,内存泄露是一个常见的挑战。本文将探讨如何对于一个陌生项目进行内存排查和优化的方法。
背景
首先项目最初不是自己写的,项目接手一段时间后才发现了问题,往往这种情况不能第一时间定位到具体代码,需要根据用户反馈以及工具排查。
刚开始用户反馈接口卡顿,影响正常使用,我这边查看日志发现没有异常报错,就尝试重启应用,重启后解决问题。
分析问题
我尝试了以下步骤
- 由于是代理服务,我以为是请求过多,导致服务日志写入过多导致,所以我只保留了严重错误日志,运行一段时间后发现无效
- 由于前不久做过一次容器化部署,程序跑一段时间后会生成
core.42
文件,文件很大,有30G, 该文件是进程崩溃的内存和寄存器的快照。我又尝试不使用docker
部署,运行一段时间后发现无效 - 使用
pm2 list
发现进程的内存使用异常高,高达3.4GB,这才发觉可能是内存泄露导致程序崩溃
arduino
pm2 list
┌────┬─────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├────┼─────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ erra │ default │ 0.6.53 │ cluster │ 30696 │ 2h │ 23 │ online │ 0% │ 3.4gb │ root │ disabled │
└────┴─────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
内存泄漏分析
- 利用Node.js内置工具: Node.js提供了一些内置工具来分析内存使用情况,如
--inspect
参数。启动Node.js应用程序时加上该参数,并使用Chrome开发者工具的Memory选项来观察内存使用情况。
bash
node --inspect --heapsnapshot-signal=SIGUSR2 ./bin/erra.js start
-
启动项目后,先在Menory中创建一个初始的快照
-
然后进行多次操作(根据项目流程操作),在创建一个快照进行内存对比,发现内存翻倍的增加
-
通过 Retained Size 排序,展开头几项看看,展开到能看到具体对象或者方法为止
通过展开(closure)也可以定位到文件,到此基本可以确定是 proxy-server.js文 件 与proxyReq 对象相关出现了内存泄露,由于我是本地调试,所以可以通过点击文件名跳转到源码处
解决问题
通过代码发现,确实存在漏洞, 这种情况属于事件监听器未移除 (由于初版代码不是自己写的,不能一眼发现此处有问题。)
虽然这里可以改成监听事件处理后释放就可以解决,但是其实这里只需监听一次就行避免频繁监听并移除
验证结果
为保证是否还存在其他问题,需要重复上面的步骤,重新验证是否还存在内存泄露。经过多次模拟操作之后,内存稳定在 16.6M 左右,确定没问题后在发布
结论
由于代码可以在本地复现,所以排查过程还是比较顺利的。如果是盲排重点从:定时器未清除、事件监听器未移除、未释放资源 方向入手。