对于持续运行的服务进程(daemon),必须及时释放再也不用到的内存。不然,内存占用愈来愈高,轻则影响系统性能,重则致使进程崩溃。 对于再也不用到的内存,没有及时释放,就叫作内存泄漏(memory leak)html
node.js中V8中的内存分代:node
启动node进程时添加参数便可 node --max-old-space-size=1700 <project-name>.js 调整老生代内存限制,单位为MB(貌似最高也只能1.8G的样子)(老生代默认限制为 64/32 位 => 1400/700 MB)git
node --max-new-space-size=1024 <project-name>.js 调整新生代内存限制,单位为KB(老生代默认限制为 64/32 位 => 32/16 MB) 接!github
内存回收时使用的算法:算法
Scavenge 算法(用于新生代,具体实现中采用 Cheney 算法)chrome
Mark-Sweep & Mark-Compact(用于老生代的回收算法)数组
buffer 声明的都为堆外内存,它们是由系统限定而非 V8 限定,直接由 C++ 进行垃圾回收处理,而不是 V8,在进行网络流与文件 I/O 的处理时,buffer 明显知足它们的业务需求,而直接处理字符串的方式,显然在处理大文件时有心无力。因此由 V8 处理的都为堆内内存。浏览器
一、浏览器方法缓存
二、命令行方法 使用 Node 提供的 process.memoryUsage 方法。网络
console.log(process.memoryUsage()); // 输出 { rss: 27709440, // resident set size,全部内存占用,包括指令区和堆栈 heapTotal: 5685248, // "堆"占用的内存,包括用到的和没用到的 heapUsed: 3449392, // 用到的堆的部分 external: 8772 // V8 引擎内部的 C++ 对象占用的内存 }
判断内存泄漏,以heapUsed字段为准。
意外的全局变量
function foo(arg) { bar = "this is a hidden global variable"; // winodw.bar = ... } 或者 function foo() { this.variable = "potential accidental global"; } // Foo 调用本身,this 指向了全局对象(window) // 而不是 undefined foo();
解决方法: 在 JavaScript 文件头部加上 'use strict',使用严格模式避免意外的全局变量,此时上例中的this指向undefined。
尽管咱们讨论了一些意外的全局变量,可是仍有一些明确的全局变量产生的垃圾。它们被定义为不可回收(除非定义为空或从新分配)。尤为当全局变量用于临时存储和处理大量信息时,须要多加当心。若是必须使用全局变量存储大量数据时,确保用完之后把它设置为 null 或者从新定义。与全局变量相关的增长内存消耗的一个主因是缓存。缓存数据是为了重用,缓存必须有一个大小上限才有用。高内存消耗致使缓存突破上限,由于缓存内容没法被回收。
被遗忘的计时器或回调函数
如计时器的使用:
var someResource = getData(); setInterval(function() { var node = document.getElementById('Node'); if(node) { // 处理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); } }, 1000);
定义了一个someResource变量,变量在计时器setInterval内部一直被引用着,成为一个闭包使用,即便移除了Node节点,因为计时器setInterval没有中止。其内部仍是有对someResource的引用,因此v8不会释放someResource变量的。
var element = document.getElementById('button'); function onClick(event) { element.innerHTML = 'text'; } element.addEventListener('click', onClick);
对于上面观察者的例子,一旦它们再也不须要(或者关联的对象变成不可达),明确地移除它们很是重要。老的 IE 6 是没法处理循环引用的。由于老版本的 IE 是没法检测 DOM 节点与 JavaScript 代码之间的循环引用,会致使内存泄漏。
可是,现代的浏览器(包括 IE 和 Microsoft Edge)使用了更先进的垃圾回收算法(标记清除),已经能够正确检测和处理循环引用 了。即回收节点内存时,没必要非要调用 removeEventListener 了。(不是很理解)
对DOM 的额外引用
若是把DOM 存成字典(JSON 键值对)或者数组,此时,同一个 DOM 元素存在两个引用:一个在 DOM 树中,另外一个在字典中。若是要回收该DOM元素内存,须要同时清除掉这两个引用。
var elements = { button: document.getElementById('button'), image: document.getElementById('image'), text: document.getElementById('text') }; document.body.removeChild(document.getElementById('button')); // 此时,仍旧存在一个全局的 #button 的引用(在elements里面)。button 元素仍旧在内存中,不能被回收。
若是代码中保存了表格某一个 <td> 的引用。未来决定删除整个表格的时候,咱们觉得 GC 会回收除了已保存的 <td> 之外的其它节点。实际状况并不是如此:此 <td> 是表格的子节点,子元素与父元素是引用关系。因为代码保留了 <td> 的引用,致使整个表格仍待在内存中。
因此保存 DOM 元素引用的时候,要当心谨慎。
闭包
var theThing = null; var replaceThing = function () { var originalThing = theThing; var unused = function () { if (originalThing) console.log("hi"); }; theThing = { longStr: new Array(1000000).join('*'), someMethod: function () { console.log(someMessage); } }; }; setInterval(replaceThing, 1000);
代码片断作了一件事情:每次调用 replaceThing ,theThing 获得一个包含一个大数组和一个新闭包(someMethod)的新对象。同时,变量 unused 是一个引用 originalThing 的闭包(先前的 replaceThing 又调用了 theThing )。思绪混乱了吗?最重要的事情是,闭包的做用域一旦建立,它们有一样的父级做用域,做用域是共享的。someMethod 能够经过 theThing 使用,someMethod 与 unused 分享闭包做用域,尽管 unused 从未使用,它引用的 originalThing 迫使它保留在内存中(防止被回收)。当这段代码反复运行,就会看到内存占用不断上升(新建的多个originalThing一直被保存在内存中),垃圾回收器(GC)并没有法下降内存占用。本质上,闭包的链表已经建立,每个闭包做用域携带一个指向大数组的间接的引用,形成严重的内存泄漏。
这时候应该在 replaceThing 的最后添加 originalThing = null,主动解除对象引用。
timeline 标签擅长作这些。在 Chrome 中打开例子,打开 Dev Tools ,切换到 timeline,勾选 memory 并点击记录按钮,而后点击页面上的 The Button 按钮。过一阵中止记录看结果:
两种迹象显示出现了内存泄漏,图中的 Nodes(绿线)和 JS heap(蓝线)。Nodes 稳定增加,并未降低,这是个显著的信号。
JS heap 的内存占用也是稳定增加。因为垃圾收集器的影响,并不那么容易发现。图中显示内存占用忽涨忽跌,实际上每一次下跌以后,JS heap 的大小都比原先大了。换言之,尽管垃圾收集器不断的收集内存,内存仍是周期性的泄漏了。
https://github.com/yygmind/blog
http://www.cnblogs.com/vajoy/p/3703859.html
https://blog.csdn.net/yolo0927/article/details/80471220