最近在系统的看Node,补了一些以前没有深入的东西,复习整理一下~
V8将堆分为新生代和老生代,新生代空间中的对象较小,垃圾回收快,在新生代存活的对象会进入老生代空间。
新生代将空间一分为二,将存活的对象复制到另一个空间,然后替换替换回去,速度快但是空间花销大。
老生代空间垃圾回收分位标记清除和标记整理两个阶段。
标记清除:老生代空间活对象存在标记,没有被标记的会被清除。
标记整理:为了解决清除后的内存碎片问题,将存活的对象进行整理。
内存泄漏可能存在的原因:
内存监测的方法:
Node中使用process.memoryUsage可以查看进程内存使用情况
rss(resident set size):RAM 中保存的进程占用的内存部分,包括代码本身、栈、堆。
heapTotal:堆中总共申请到的内存量。
heapUsed:堆中目前用到的内存量,判断内存泄漏我们主要以这个字段为准。
external: V8 引擎内部的 C++ 对象占用的内存。
堆一般用来存储数组和对象,栈用来存储方法和简单变量。
浏览器中可使用chrome的开放工具中的performance & memory监测。
performance绘制内存使用图,
memory显示具体内存占用情况。
一个简单的例子:
1 | function foo(element, a, b) { |
上述代码存在闭包,因此会保留element、a、b的引用,直到引用闭包处被回收。
浏览器使用引用计数标记是否为活对象,如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。
稍复杂的例子:1
2
3
4
5
6function fa() {
var e = document.getElementById("id");
e.event = function () {
alert("hello word");
};
}
存储e的堆空间引用计数为2,存储匿名函数的堆空间引用为2,当e自动回收引用-1变为1,在外若执行e.event = null , 则匿名函数的堆空间引用为1,e和匿名函数的引用此时都不为0,都无法回收。
1 | let a = new map(); |
这种情况下无法回收a[b]的堆内存,但是通过使用weakMap在b=null时可以自动回收map中b作为key值对应的存储空间。
引用:
1. 也议 js闭包和ie内存泄露原理
2. JavaScript 内存泄漏教程
3. Nodejs中的内存管理和V8垃圾回收机制