内存泄露怎样产生的
造成内存泄露的根本原因就是我们写的代码中存在某些对象长期占用内存,得不到释放,且这个对象占用的内存会逐步增加,导致 v8 无法回收,从而造成的服务的异常和不稳定,甚至是服务的中断和崩溃。
内存泄露在浏览器端不是很敏感,如果是在服务端就不得不考虑这个问题。因为内存泄露具有潜伏性,而且非常不明显,在时间的推移下才能慢慢的发现异常,内存占用不断增加,等到发现的时候已经来不及采取有效的解决方案进行处理,只能重启服务来暂时处理这种风险。所以在服务上线前就需要进行有效的检测。
node-headdump
本文主要来说下使用 node-headdump
进行内存泄露的定位。
该模块主要是一个抓取当前内存的快照(存储为JSON文件)信息,包括所有的字符,对象和函数所占用内存的情况。
官方地址:https://github.com/bnoordhuis/node-heapdump
这个模块的使用非常简单。
- 安装
npm install heapdump //如果遇到权限问题, 可以使用 npm install heapdump --unsafe-perm
- 在代码中引入
const heapdump = require('heapdump');
既然要使用 node-heapdump
对内存泄露进行定位,我需要先构造一个有问题的代码,然后最终已服务的形式启动。
下面代码中,变量 arr
会常驻内存,无法释放,在服务器每次接收请求的时候都会向 arr
写入一条数据
//内存泄露定位 const http = require('http'); const heapdump = require('heapdump'); const arr = []; const runleak = function () { arr.push('node leak'+Math.random()); } http.createServer((req,res)=>{ //模拟大内存对象 for(var i=0;i<100000;i++){ runleak(); } // 创建快照 方法1 通过代码创建快照 heapdump.writeSnapshot(Date.now() + '.heapsnapshot'); //end res.writeHead(200,{'content-type':'text/plain'}); res.end('hello , test node leak'); }).listen(9527); console.log('server 9527 start... http://localhost:9527');
创建快照的方式有两种
- 代码写入
可以设置写入的时机和快照存储的位置。
heapdump.writeSnapshot(function(err, filename) { console.log('dump written to', filename); });
- 命令写入
快照将会存储在文件目录下以 heapdump-<sec>.<usec>.heapsnapshot
的格式存放.
$ kill -USR2 <pid>
首先需要查找到当前的进程 id
$ ps aux | grep node //或者根据端口号查找 $ lsof -i tcp:9527
查看分析
其实生成的快照就是一个 json
文件,可以通过Chrome的开发者工具进行查看。
f12
打开开发者工具- 打开
Memory
面板 - 在
Profiles
上右键 ,点击load...,打开快照文件
加载快照文件后就能看到大量占用内存的数据,然后根据这些信息找到存在内存泄露的代码。
其他工具
下面是一些常见的用于排查 node 应用的内存泄露工具,有兴趣的可以了解下
- v8-profiler
- node-mtrace
- dtrace
- node-memwatch
共勉-寄语
当才华还撑不起你的野心的时候,就应该静下心来学习。当能力还驾驭不了你的目标的时候,就应该沉下心来历练。问问自己,想要怎样的人生。