前言
每门语言都有自己的垃圾回收机制,有需要手动清理的有自动的清理的,而js的垃圾回收是全自动的,但是它的内部机制是如何工作的呢?下面让我们一步步来解析
js是如何产生垃圾数据的
我们知道js中的内存是通过堆栈构成的,比如我在window上定义了一个a对象
window.a = { name: 'hello' };
在内存中它是这样的
栈中一般都是保存的对象的引用地址,而堆中则是这个对象的数据,如果我们此时将代码改成这样
window.a = new Date();
这个时候a的引用地址则是new Date() 数据的地址
那么此时原来的Object对象已经变成垃圾数据了,有了垃圾数据那GC具体是如何做清理的
数据的清理回收
首先我们介绍两个概念
1 活动对象:GC从根访遍历访问对象,GC将访问到的对象标记为活动对象
2 非活动对象: 如果虫GC Root没有遍历到的标记为非活动对象
GC Root 在浏览器中比如是window对象,栈上存放的变量等这些,
在堆空间中为了提高垃圾回收的效率其实会将堆内存分为两个区间一个是新生区一个是老生区,他们的区别是新生区主要负责一些存活时间比较短的数据对象比如函数的内部变量,当函数销毁时对象数据也会被销毁,通常他们占用的空间比较小1~8M,而存活时间比较长的和比较大的数据对象则放在老生区
对于不同的区域垃圾回收器也是不一样的
新生区主要有副垃圾回收器负责,老生区只要是主垃圾回收器,他们各自回收的算法和机制都是不一样的,下来我们分别看一下两个区域的垃圾回收器的回收方式
新生区
新生区回收的算法是Scavenge算法,将新生区的区域划分为两个区域,对象区和空闲区,新产生的对象都将放在对象区,当对象区的对象快要沾满的时候,就会执行一次GC,将对象区中的活动对象标记出来, 副垃圾回收器随后在将这些活动对象按照一定顺序复制到空闲区,复制完成后再将这两个区域反转,原来的对象区变成了空闲区,而原来的空闲区变成了对象区。副垃圾回收器将反复执行这一操作,同时还会有对象晋升策略,将多次放到对象区的对象复制到老生区中,至此便是副垃圾回收器的全部动作
老生区
老生区使用标记 - 清除(Mark-Sweep)算法,将老生区中标记的非活动对象直接清除,但是在多次执行清除之后,内存中的空间将是不连续的,当需要一片连续的储存空间的时候这个时候就有问题了,所以主垃圾回收器又引入了标记 - 整理(Mark-Compact)算法 将活动对象都移向一端,然后再将另一端的数据清除掉,释放内存
整过过程如下图