V8引擎如何回收垃圾
V8的内存分配
和操作系统有关,64位操作系统,V8最多使用大约1.4GB的内存空间。
为什么要设计为1.4GB,而不设计更大的内存空间?
- JS最初设计是在浏览器上运行的,浏览器上的JS不持久,1.4G足够使用。
- JS回收垃圾会暂停所有代码执行,内存越大回收时占用的时间越多。
V8将自己的内存空间分为两部分:新生代内存空间和老生代内存空间。

新生代内存空间
新生代内存空间占用64MB,分为semi space From和semi space To两部分。
存放的是存活时间比较短的变量,会频繁发生垃圾回收。
本质是复制操作。
流程:
-
新建变量存放在From中,回收时标记From中活着的变量,将To空间清除,然后把活着的变量复制到To中。
-
后面再次回收时标记To中活着的变量,将From空间清除,然后把活着的变量复制到From中。
为什么使用这种设计?用空间换取时间,这种设计速度快,不需要进行内存整理,直接清空复制。
老生代内存空间
老生代内存空间占用1.4GB。本质是标记删除整理。

流程:
-
标记已死变量,删除已死变量。产生内存碎片(斑马纹)-> 标记清除(Mark Sweep)
-
整理内存空间 -> 标记合并(Mark Compact)
Mark Sweep
Mark Sweep是将需要被回收的对象进行标记,在垃圾回收运行时直接释放相应的地址空间,如下图所示(红色的内存区域表示需要被回收的区域):

如上图所示,使用 Mark Sweep 进行垃圾回收会产生一个问题,就是垃圾回收后内存会出现不连续的情况,为了解决这个问题,出现了 Mark Compact 方案。
Mark Compact
Mark Compact 的思想有点像新生代垃圾回收时采取的 Cheney 算法:将存活的对象移动到一边,将需要被回收的对象移动到另一边,然后对需要被回收的对象区域进行整体的垃圾回收。

新生代到老生代
满足两个条件:
- 变量在新生代内存空间经历过一次复制。
- 新生代内存空间中To部分或From部分已经占用25%。
变量处理
- 内存主要就是存储变量等数据的;
- 局部变量当程序执行结束,且没有引用的时候就会死掉;
- 全局对象会始终存活到程序运行结束;
示例1:
function f () {
var a = ''
}
f(); // f 执行结束 a 就会被回收
示例2:
function f () {
var a = '';
return a;
}
var b = f(); // f 执行结束 a 不会被回收,因为外层作用域还有 a 的引用 b。
查看V8内存的使用情况
通过node中的process.memoryUsage()查看
- 命令行输入node回车,再输入process.memoryUsage();,返回值可查看。

- rss: V8申请到的总占用空间;
- heapTotal: 堆总内存;
- heapUsed: 已使用的内存;
- external: node专有(底层是c,额外申请到的c++内存 );
- 代码查看:
function getMemory() {
const memory = process.memoryUsage();
const format = function(bytes) {
return (bytes/1024/1024).toFixed(2) + 'MB';
}
console.log('Process: heapTotal ' + format(memory.heapTotal) +
' heapUsed ' + format(memory.heapUsed) +
' rss ' + format(memory.rss));
};
通过浏览器查看
- 在Chrome中F12调试工具Memory查看。
- Chrome中console中输入window.performance查看

内存优化
-
尽量不要定义全局变量。
-
全局变量使用完成后销毁。
示例:
a = undefined; // 建议
delete a; // 不建议使用,严格模式下会出问题
- 使用匿名自执行函数变全局为局部。
示例:
(function () {}())
var size = 20 * 1024 * 1024;
var arr1 = new Array(size);
var arr2 = new Array(size);
var arr3 = new Array(size);
var arr4 = new Array(size);
var arr5 = new Array(size);
var arr6 = new Array(size);
var arr7 = new Array(size);
var arr8 = new Array(size);
var arr9 = new Array(size);
getMemory(); // 内存溢出 极限 8 个

- 闭包?闭包并不会影响内存。
示例:
for (let i = 10000; i < 10100; i++) {
setTimeout(function () {
console.log(i);
getMemory(); // 内存占用 4.x MB
})
}
// 闭包形式
for (let i = 10000; i < 10100; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
getMemory(); // 内存占用 4.x MB
})
})(i)
}
欢迎关注我的公众号,谢谢大家!
