JavaScript 中的垃圾回收机制(GC)
1. 垃圾回收相关概念
垃圾: 没有被引用的对象,就是垃圾对象。
垃圾回收:
- 销毁垃圾对象,释放内存,称为垃圾回收。
- 像C、C++ 等编程语言需要手动回收垃圾。
- 像Paython、JavaScript、Java 等编程语言自动回收。
变量的生命周期:(何时会被回收)
- 修改变量的值,变量不再引用某个对象,该对象变为垃圾
- 执行执行结束,变量自动销毁,原来所指向的对象不再被引用,变为垃圾。
2. 垃圾没有及时回收的后果
- 垃圾对象没有被清除,导致内存空间被占用, 造成内存泄漏。
- 内存空间越占越多,导致程序卡顿甚至死机。
3. JavaScript 垃圾回收的常见算法
- 引用计数
- 标记清除
引用计数(现在很少使用)
1. 原理
- 每个对象都有一个引用标记
- 增加对该对象的引用(给变量或属性赋值),该对象的引用标记+1
- 减少对该对象的引用(变量或属性该值,变量属性被销毁),该对象的引用标记-1
- 当引用标记为 0 的时候,该对象变为垃圾对象,被回收
var a = [10,20,30,40]; // 此时数组的引用计数是 1
var b = a; // 数组引用计数变为 2
a = null; // 修改变量 a 的值; 数组的引用计数变为 1
b = 10000; // 修改变量 b 的值; 数组的引用技术变为0,被销毁
// 直接量 用完即销毁
[1000,2000,3000,4000,6000];
2. 优缺点:
- 垃圾回收及时,计算效率高
- 互相引用的两个对象,会永远无法被销毁,常驻内存,造成内存泄漏
var obj1 = {name:'x'}; // x引用计数是 1
var obj2 = {name:'y'}; // y的引用计数是 1
obj1.child = obj2; // y引用计数是 2
obj2.child = obj1; // x的引用计数是 2
// 修改变量 obj1的值; x引用计数是 1
// obj1 = null;
// 修改变量 obj2 的值; y的引用计数 1
// obj2 = null;
标记清除(常用)
1. 原理
- 系统不断循环执行标记清除操作
- 每次标记清除分为两个阶段:
- 标记阶段:从全局对象开始,向下遍历属性以及属性的属性(树状递归遍历),能够访问到的对象,进行标记,称为可访问对象。
- 清除阶段:线性遍历内存中所有的对象,将没有标记的对象就是垃圾,进行回收。
- 下一轮去掉所有对象的标记,进行重新标记清除
2. 优缺点
- 所有的垃圾对象都可以被回收,不会造成内存泄漏
- 不停地循环进行进行树状递归遍历,执行效率相对较低
var obj1 = {name:'x'}; // x标记
var obj2 = {name:'y'}; // y标记
obj1.child = obj2; // y标记
obj2.child = obj1; // x标记
//改变值,没有引用,没有标记
// obj1 = null;
// obj2 = null;