作者介绍
马涛,年加入去哪儿网技术团队,目前在目的地事业部,负责H5、小程序类应用开发。个人对移动端技术领域和前后端工程化有浓厚兴趣,勇于探索实践追求极致。
前言无论是Java中的java.lang.OutOfMemoryError,还是ASP.NET中的System.OutOfMemoryException,偶尔一次内存泄漏对于传统的后端工程师来讲可谓是司空见惯的事情。
然而在Node编程中这个问题似乎并不常见或者说由于使用ProcessManagers(进程管理器)做守护、重启,掩盖了这个问题的存在。之前一直是“此漏只应Java有,Node哪的几回闻”的心态,直到在一次测试环境中遇到它。
难以发现的OOM//让我们从一条日志说起...
qnc:pm2-messengerprocessexitwithcode:1
“早上刚部署的服务,怎么中午访问就了?“(一个莫名其妙令人悲伤的故事...)
完全的出乎意料,这是个用nohupnpx启动的node测试服务。难道是代码有BUG产生了“致命”异常导致进程退出了?于是开始了我的找寻真理(BUG)之旅!
首先想到的是监听尽可能多的进程事件!
//Node官方文档关于ExitCodes有一条如下:
//1UncaughtFatalException-Therewasanuncaughtexception,anditwasnothandledbyadomainoranuncaughtExceptioneventhandler.
process.on(uncaughtException,(err,origin)={
console.error(err,origin)
})
//俩小时之后,进程退出,日志并没有异常捕获相关的输
进程退出总有个信号啥的吧,肯定是我的监听还不够!
functionhandle(signal){
console.log(`Received${signal}`);
}
process.on(SIGINT,handle);
process.on(SIGHUP,handle);
process.on(SIGBREAK,handle);
process.on(SIGTERM,handle);
//能用的都用上了,又是俩小时,日志无果
一时间毫无头绪,进程总不该是被kill掉的吧?有人登机器杀进程?不该这么无聊啊!不是人为难道是操作系统干的?
从系统日志(/var/log/messages)中有了重大发现:
kernel:nodeinvokedoom-killer:gfp_mask=0xda,order=0,oom_score_adj=0
kernel:nodecpuset=/mems_allowed=0
...(此处省略若干行内存日志)
...(还有进程列表日志)
kernel:Outofmemory:Killprocess(node)scoreorsacrificechild
kernel:Killedprocess(node)total-vm:kB,anon-rss:kB,file-rss:0kB
至此,终于识得庐山真面目,NodeJS遇上了OOM!
从日志可以看出node触发了oom-killer,然后系统报告了当时的内存及进程详细情况,为了保护系统正常运转最终选择killnode(process)。Linux的内存分配和管理比较有意思,感兴趣的同学可以扒一扒相关的关键词深入学习下(over