【行业动态】如何分析 Node.js 中的内存泄漏?



内存泄漏(Memory Leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。如果内存泄漏的位置比较关键,那么随着处理的进行可能持有越来越多的无用内存,这些无用的内存变多会引起服务器响应速度变慢,严重的情况下导致内存达到某个极限(可能是进程的上限,如v8的上限;也可能是系统可提供的内存上限)会使得应用程序崩溃。
传统的C/C++中存在野指针,对象用完之后未释放等情况导致的内存泄漏。而在使用虚拟机执行的语言中如Java、JavaScript由于使用了GC(GarbageCollection,垃圾回收)机制自动释放内存,使得程序员的精力得到的极大的解放,不用再像传统语言那样时刻对于内存的释放而战战兢兢。
但是,即便有了GC机制可以自动释放,但这并不意味着内存泄漏的问题不存在了。内存泄漏依旧是开发者们不能绕过的一个问题,今天让我们来了解如何分析Node.js中的内存泄漏。

GC in Node.js

+H xe+I"wXq`8Sf0
Node.js使用V8作为JavaScript的执行引擎,所以讨论Node.js的GC情况就等于在讨论V8的GC。在V8中一个对象的内存是否被释放,是看程序中是否还有地方持有该对象的引用。
在V8中,每次GC时,是根据root对象(浏览器环境下的window,Node.js环境下的global)依次梳理对象的引用,如果能从root的引用链到达访问,V8就会将其标记为可到达对象,反之为不可到达对象。被标记为不可到达对象(即无引用的对象)后就会被V8回收。更多细节,可以参见alinode的 解读V8 GC。
了解上述的点之后,你就会知道,在Node.js中内存泄露的原因就是本该被清除的对象,被可到达对象引用以后,未被正确的清除而常驻内存。

内存泄漏的几种情况

6V]h+@.Df1e"?@.V9g0
1.全局变量,这种比较简单的原因,全局变量直接挂在 root 对象上,不会被清除掉。
2.闭包,闭包会引用到父级函数中的变量,如果闭包未释放,就会导致内存泄漏。需要注意的是,实际的业务情况可能是挂在某个可以从root追溯到的对象上导致的。
3.事件监听,Node.js的事件监听也可能出现的内存泄漏。例如对同一个事件重复监听,忘记移除(removeListener),将造成内存泄漏。这种情况很容易在复用对象上添加事件时出现,所以事件重复监听可能收到警告。
例如,Node.js中Agent的keepAlive为true时,可能造成的内存泄漏。当Agent keepAlive为true的时候,将会复用之前使用过的 socket,如果在socket上添加事件监听,忘记清除的话,因为socket的复用,将导致事件重复监听从而产生内存泄漏。
原理上与前一个添加事件监听的时候忘了清除是一样的。在使用 Node.js的http模块时,不通过keepAlive复用是没有问题的,复用了以后就会可能产生内存泄漏。所以,你需要了解添加事件监听的对象的生命周期,并注意自行移除。
4.其他原因,还有一些其他的情况可能会导致内存泄漏,比如缓存。在使用缓存的时候,得清楚缓存的对象的多少,如果缓存对象非常多,得做限制最大缓存数量处理。还有就是非常占用CPU的代码也会导致内存泄漏,服务器在运行的时候,如果有高CPU的同步代码,因为Node.js是单线程的,所以不能处理处理请求,请求堆积导致内存占用过高。

如何避免内存泄漏存储产业技术创新战略联盟2G-Z RT-h$z `1C

在工作中,代码混合上业务以后就不一定能很清楚的看出内存泄漏了,还是得依靠工具来定位内存泄漏。另外下面是一些避免内存泄漏的方法。
ESLint 检测代码检查非期望的全局变量。使用闭包的时候,得知道闭包了什么对象,还有引用闭包的对象何时清除闭包。最好可以避免写出复杂的闭包,因为复杂的闭包引起的内存泄漏,如果没有打印内存快照的话,是很难看出来的。
绑定事件的时候,一定得在恰当的时候清除事件。在编写一个类的时候,推荐使用 init 函数对类的事件监听进行绑定和资源申请,然后 destroy 函数对事件和占用资源进行释放。


  最新报道
  • 存储产业技术创新战略联盟祝 
  • 【行业动态】如何分析 Node. 
  • 【行业动态】存储级内存不会 
  • 【行业动态】开启崭新的区块 
  • 【行业动态】日本研发出6G芯 
  • 存储产业技术创新战略联盟祝 
  • 存储联盟于成都成功举办技术 
  • 【联盟活动】技术沙龙活动将 
  • 如何解决云中容器数据存储的 
  • 选择无人机的存储卡 一定要 
  •  
    
     
    存储产业技术创新联盟 | 联系我们 | 京ICP备12012807-2号
    © 2012 Allstor