线上环境竟然遇到了内存泄露,通过 3 天的摸索,算是解决了:javascript
更换 pm2 版本,由 v3.5.1 降为 v3.4.1
对于这个结论仍是不太满意,算是歪打正着。不过仍是记录下这几天积累的经验,说不定对正在看此文的你会有帮助。html
有幸,我此次有充足的时间去排查,没有其余事情干扰。但线上出现内存泄漏,解决起来比业务代码 bug 更难解决,那种头皮发麻的难受。缘由以下:前端
无论有没有精力 or 能力,我认为 解决内存泄漏的最佳实践是:积极的心态 + 冷静的问题定位。(没错,这是对目前没有解决问题的你说的)java
能够看下这篇文章 Finding And Fixing Node.js Memory Leaks: A Practical Guidenode
Anyway,其实无论什么缘由,主要仍是 各类引用 得不到 V8 GC 的释放。扔一段很经典的代码:python
function calc(data) { return Math.round((data / 1024 / 1024) * 100) / 100 + " MB"; } function logger() { let mem = process.memoryUsage(); console.log(new Date(), "memory now:", calc(mem.rss)); } var theThing = null; var replaceThing = function() { logger(); var originalThing = theThing; var unused = function foo() { if (originalThing) { console.log("未被调用,但 originalThing 有个 someMethod 的引用"); } }; theThing = { longStr: new Array(1000000).join("*"), someMethod: function() { console.log("没作任何事情,但我是闭包"); } }; console.log("parse"); }; setInterval(replaceThing, 10);
执行没多久,就从 20M 飚到 几百 M。究其缘由,仍是由于闭包引用没有及时被销毁。linux
具体缘由以下:c++
虽然 unused 没有被调用,可是其中包含 originalThing 并指向 theThing ,theThing 在定义时有个 someMethod 方法,其就是个闭包(能够访问到 originalThing) ,该闭包在 unused 中因为引用了 originalThing 一直没有被释放。git
若是你对这块有查询过相关资料, heapdump 这模块应该频繁出现,这里说下如何使用它来监控项目的内存状况。固然还有 memwatch ...github
npm install heapdump -S
若是你足够幸运,确定会出现以下问题:
error: #error This version of node/NAN/v8 requires a C++11 compiler
原版本太低,须要更新 linux 系统的 gcc 等相关库。
参考以下安装说明:
# 安装 repo 仓库 wget http://people.centos.org/tru/devtools-2/devtools-2.repo mv devtools-2.repo /etc/yum.repos.d # 安装新库 yum install devtoolset-2-gcc devtoolset-2-binutils devtoolset-2-gcc-c++ # 备份原库 mv /usr/bin/gcc /usr/bin/gcc-4.4.7 mv /usr/bin/g++ /usr/bin/g++-4.4.7 mv /usr/bin/c++ /usr/bin/c++-4.4.7 # 建立快捷方式,将新库链到须要目录 ln -s /opt/rh/devtoolset-2/root/usr/bin/gcc /usr/bin/gcc ln -s /opt/rh/devtoolset-2/root/usr/bin/c++ /usr/bin/c++ ln -s /opt/rh/devtoolset-2/root/usr/bin/g++ /usr/bin/g++ # 检查版本,肯定生效 gcc --version
固然系统是 window 可能还会有更坑的问题:安装 node-gyp、python 出错。
推荐以下 npm 模块:
windows-build-tools
一键安装相关组件依赖(咱们只要静静的等待,由于时间有些久)。他会帮你安装 window 对应的 NET Framework,python 这些插件。
npm install --global --production windows-build-tools
线上很简单的作了一个控制台输出,用于定位问题:
var heapdump = require("heapdump"); let startMem = process.memoryUsage(); function calc(data) { return Math.round((data / 1024 / 1024) * 10000) / 10000 + " MB"; } // 使用的是 koa router.all("/foo", async (ctx, next) => { let mem = process.memoryUsage(); logger.debug("memory before", calc(startMem.rss), "memory now:", calc(mem.rss), "diff increase", calc(mem.rss - startMem.rss)); // ... });
这样就能实时看到系统的内存消耗(对比刚启动时):
2019-09-06 16:30 +08:00: [2019-09-06T16:30:06.700] [DEBUG] transfer - memory before 55.5898 MB memory now: 95.1484 MB diff increase 39.5586 MB 2019-09-06 16:30 +08:00: [2019-09-06T16:30:07.724] [DEBUG] transfer - memory before 56.2148 MB memory now: 69.8438 MB diff increase 13.6289 MB 2019-09-06 16:30 +08:00: [2019-09-06T16:30:10.406] [DEBUG] transfer - memory before 56.2148 MB memory now: 70.5977 MB diff increase 14.3828 MB 2019-09-06 16:30 +08:00: [2019-09-06T16:30:11.018] [DEBUG] transfer - memory before 55.5898 MB memory now: 95.4219 MB diff increase 39.832 MB 2019-09-06 16:30 +08:00: [2019-09-06T16:30:12.827] [DEBUG] transfer - memory before 55.5898 MB memory now: 95.6797 MB diff increase 40.0898 MB 2019-09-06 16:30 +08:00: [2019-09-06T16:30:12.952] [DEBUG] transfer - memory before 55.5898 MB memory now: 94.9688 MB diff increase 39.3789 MB
添加个快照路由,在按照须要抓取内存此刻使用状况:
router.all("/snapshot", async (ctx, next) => { heapdump.writeSnapshot("./dump-" + Date.now() + ".heapsnapshot", function(err) { if (err) console.error(err); }); });
导入到 chrome 的 profile 面板中,对比先后两个文件的变化,定位问题
一切顺利就能很快定位到问题代码。但实际要更困难,更摸不着头脑。
若是按照上述的“检查”操做仍是没有定位到问题点,可能这段会对你有所帮助。
首先要知道本身着手的项目的用途,所用技术,它对你排查问题更有指向意义。
好比:此项目是基于 node 的中间层服务,对 api 接口进行转换。用于由后端 api 服务的“升级”平滑各客户端的发版时差。(服务可能在 api 调用上有性能瓶颈?)
技术栈:sequelize + koa + pm2 (熟悉项目的主要框架,从大技术方向着手)
有幸有个 项目 B 和此项目相似,技术上略有差别。
综上所述,就猜想了几个可能的 内存泄露 缘由(附参考文章):
代码问题
代码逻辑全局缓存问题
访问量(项目 A 高于 项目 B)
对数据库的查询的冲击(sequelize)
日志读写堆积(log4js)
第三方依赖
pm2
可能缘由 | 测试方式 | 验证结果 | 备注 |
---|---|---|---|
代码问题 | ab 压测 | ok | 但需增长重视 |
sequlize 版本问题 | ab 压测 | ok | 暂不尝试。目前使用 4.42.0,线上没法承担更换版本的风险 |
Ladash _.template | ab 压测 | ok | 保持现状 |
log4js 有背压问题 | ab 压测 | ok | pm2 & log4js 使用不够友好,在有替代方案前(好比 winston),保持现状 |
pm2 版本问题 | 线上下降版本 3.5.1 -> 3.4.1 | 待验证 | 项目 B 使用 3.4.1 |
这两天从线上状况上看,修改 pm2 版本后,内存泄漏获得控制。下面由此结论反推验证步骤:
TIMERWRAP 是 Node 里 Timer 相关定义,猜想是否有定时器在有规律的无限刷新占用性能。
metrics 心跳检测,是否有 setTimeout 之类的代码?
查看线上项目相关代码,的确有这段问题代码:
对于为什么这段 if/else 会形成内存泄漏,有空再研究下。估计相似 dom 的 event 绑定没有解除所致。
我只是知识点的“加工者”, 更多内容请查阅原文连接 , 同时感谢原做者的付出:
若是你以为这篇文章对你有帮助, 请点个赞或者分享给更多的道友。
也能够扫码关注个人 微信订阅号 - [ 前端雨爸 ], 第一时间收到技术文章 , 工做之余我会持续输出
最后感谢阅读, 大家的支持是我写做的最大动力