NodeJS中的内存泄露

  内存泄露(Memory Leak)指因为疏忽或错误形成程序未能释放已经再也不使用内存的状况。若是内存泄露的位置比较关键,那么随着处理的进行可能持有愈来愈多的无用内存,这些无用内存的变多会引发服务器响应速度变慢,严重的状况下致使内存达到某个极限(多是进程的上限,如V8的上限;也多是系统可提供的内存上限)会使得应用程序奔溃。
  传统的 C/C++ 中存在指针,对象在用完以后未释放等状况致使的内存泄露。而在使用虚拟机执行的语言中如 Java、Javascript 因为使用了GC(Garbage Collection,垃圾回收)机制自动释放内存,使得程序员的精力获得了极大的解放,不用再像传统语言那样时刻对于内存的释放而战战兢兢。
  可是,即便有了GC机制能够自动释放,但这并不意味着内存泄露的问题不存在了。内存泄露依旧是开发者们不能绕过的一个问题。javascript


GC in Node.js

  Node.js使用V8做为Javascript的执行引擎,因此讨论Node.js的GC状况就等同于在讨论V8的GC。在V8中一个对象的内存释放被释放,是看程序中是否还有地方持有该对象的引用。
  在V8中,每次GC时,是根据root对象(浏览器环境下的window,Node.js环境下的global)依次梳理对象的引用,若是能从root的引用链到达访问,V8就会将其标记为可到达对象,反之为不可到达对象。被标记为不可到达对象(即无引用的对象)后就会被V8回收。
  在NodeJS中内存泄露的缘由就是:本该被清除的对象,被可到达对象引用以后,未被正确的清除而常驻内存。java


内存泄露的几种状况

1、全局变量

a = 10; // 未声明对象
global.b = 11; // 全局变量引用
复制代码

这种缘由比较简单,全局变量直接挂在 root 对象上,不会被清除掉。程序员


2、闭包

function out(){
    const data = Buffer.alloc(100);
    inner = function(){
        void data
    }
}
复制代码

  闭包会引用到父级函数中的变量,若是闭包未释放,就会致使内存泄露。
  这里的例子只是简单的将引用挂在全局对象上,实际的业务状况可呢呢个是挂在某个能够从root追溯到的对象上致使的。浏览器


3、事件监听

  NodeJS的事件监听也可能出现内存泄露。例如对同一个事件重复监听,忘记移除(removeListener),将形成内存泄露。这种状况很容易在复用对象上添加事件时出现。
  例如,NodeJS中Agent的keepAlive为true时,可能形成内存的泄露。当Agent keepAlive为true时,将会复用以前使用过的socket,若是在socket上添加事件监听,忘记清除的话,由于socket的复用,将致使事件重复监遵从而致使内存泄露
  原理上与添加事件监听忘了清除是同样的。在使用NodeJS的http模块时,不经过keepAlive复用是没有问题的,复用了之后就可能产生内存泄露。因此你须要了解添加事件监听对象的生命周期,并注意自行移除。缓存


其余缘由

  还有一些其余的状况可能会致使内存泄露,好比缓存。在使用缓存的时候,得清楚缓存的对象是多少,若是缓存对象很是多,得作限制最大缓存数量处理。还有就是很是占用CPU的代码也会致使内存泄露,服务器在运行的时候,若是有高CPU的同步代码,由于NodeJS是单线程的,因此就不能处理后面的请求了,请求堆积致使内存占用太高。服务器


如何避免内存泄露

  • ESLint检测代码,排查非指望的全局变量。
  • 使用闭包的时候,得知道闭包了什么对象,还有引用闭包的对象什么时候清除闭包。最好能够避免写出复杂的闭包,由于复制的闭包引发的内存泄露,若是没有打印内存快照的话,是很难看出来的。
  • 绑定事件的时候,必定得在恰当的时候清除事件。在编写一个类的时候,推荐使用init函数对类的事件监听进行绑定和资源申请,而后destory函数对事件和占用资源进行释放。
相关文章
相关标签/搜索