1、现象
1. 以下报错
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 1: node::Abort() [/usr/local/bin/node] 2: node::OnFatalError(char const*, char const*) [/usr/local/bin/node] 3: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [/usr/local/bin/node] 4: v8::internal::Factory::NewFixedArray(int, v8::internal::PretenureFlag) [/usr/local/bin/node] 5: v8::internal::OrderedHashTable<v8::internal::OrderedHashMap, 2>::Rehash(v8::internal::Handle<v8::internal::OrderedHashMap>, int) [/usr/local/bin/node] 6: v8::internal::Runtime_MapGrow(int, v8::internal::Object**, v8::internal::Isolate*) [/usr/local/bin/node] 7: 0x2ecd2d1042fd 8: 0x2ecd2d1b251e
2.在linux服务器上输入top命令,发现该进程memery占用居高不下。
2、内存泄漏介绍
由上述问题能够确认,该node服务内存泄露. Node.js 进程的内存管理,都是有 V8 自动处理的,包括内存分配和释放。那么 V8 何时会将内存释放呢?node
在 V8 内部,会为程序中的全部变量构建一个图,来表示变量间的关联关系,当变量从根节点没法触达时,就意味着这个变量不会再被使用了,就是能够回收的了。 而这个回收是一个过程性的,从快速 GC 到 最后的 Full GC,是须要一段时间的。 另外,Full GC 是有触发阈值的,因此可能会出现内存长期占用在一个高值,也能够算是一种内存泄漏,能够从《一次 Node.js 应用内存暴涨分析》中找到例子。还有一种就是引用不释放,致使没法进入 GC 环节,而且一直产生新的占用,这通常会发生在 Javascript 层面。linux
因此,定位内存泄漏问题,通常方案就是找那些不被使用又不会被释放的变量,处理了这些变量,问题通常就能够解决了。若是是 Node.js 底层变量不释放,除了提交 issue 等待解决外,只能经过优化启动参数来解决。web
3、问题定位
一、看代码,代码里面是否存在闭包或者变量声明或者引用不当的地方。
二、是否引用node-canvas 或者echart等大型消耗cpu内存资源等依赖包
三、借助工具来分析,好比heapdump + chrome浏览器 memery分析工具
4、问题修复以heapdump为例,分析并修复内存泄漏的问题(基于express项目)。
一、项目根目录下安装heapdump
// node version > v0.8 npm install heapdump // Or, if you are running node.js v0.6 or v0.8: npm install heapdump@0.1.0
二、入口文件引入这个heapdump
// express中,在app.js中加入 const heapdump = require('heapdump');
三、重启该express服务,top(mac 上经过 top -pid xxx 查看) 命令查看linux占用状况:,
并发起少数几回请求后,输入以下命令,生成heap快照文件,记做name1chrome
kill -USR2 <pid>
四、模拟批量服务请求
因为我这个是能够提供给web调用的接口,所以用一个抓包工具charles(或者其余模拟请求工具都行),重复发1000次请求,在用top命令查看该进程memery占用状况。发现问题能够重现了。同时输入以下命令:express
// in UNIX platforms kill -USR2 <pid> // 执行上述命令,会生成一下heap快照的文件,记做name2

五、拷贝name一、name2文件到本地,并在chrome浏览器中打开分析;
6 、通过上述对比分析,能够得知代码存在的问题,而后做出相关优化。npm
好比,我这里是用到了node-canvas 和 Echart,最后发现这两个对象引用后一直没有内内存回收,致使内存溢出。代码层面作以下优化:canvas
stage.destroy(); chart.dispose(); chart = null; stage = null;
而后,启动服务,模拟1000次请求,结果发现内存占用比以前少了不少,基本上不会出现内存溢出的问题了。浏览器