# 垃圾回收

V8的垃圾回收策略主要是基于`分代式垃圾回收机制`，其根据**对象的存活时间**将内存的垃圾回收进行不同的分代，然后对不同的分代采用不同的垃圾回收算法。

## **V8的内存结构**

在V8引擎的堆结构组成中，其实除了`新生代`和`老生代`外，还包含其他几个部分，但是垃圾回收的过程主要出现在新生代和老生代，所以对于其他的部分我们没必要做太多的深入，有兴趣的小伙伴儿可以查阅下相关资料，V8的内存结构主要由以下几个部分组成：

* `新生代(new_space)`：大多数的对象开始都会被分配在这里，这个区域相对较小但是垃圾回收特别频繁，该区域被分为两半，一半用来分配内存，另一半用于在垃圾回收时将需要保留的对象复制过来。
* `老生代(old_space)`：新生代中的对象在存活一段时间后就会被转移到老生代内存区，相对于新生代该内存区域的垃圾回收频率较低。老生代又分为`老生代指针区`和`老生代数据区`，前者包含大多数可能存在指向其他对象的指针的对象，后者只保存原始数据对象，这些对象没有指向其他对象的指针。
* `大对象区(large_object_space)`：存放体积超越其他区域大小的对象，每个对象都会有自己的内存，垃圾回收不会移动大对象区。
* `代码区(code_space)`：代码对象，会被分配在这里，唯一拥有执行权限的内存区域。
* `map区(map_space)`：存放Cell和Map，每个区域都是存放相同大小的元素，结构简单(这里没有做具体深入的了解，有清楚的小伙伴儿还麻烦解释下)。

![](https://3490195898-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnQxDcxCKODvYvTUWe3%2F-Lviw3tc0vKvqVIpJ92W%2F-LviyHoRtvghncn5O74u%2Fimage.png?alt=media\&token=c5661a55-1495-4f2d-ae6e-67f5d05a946e)

上图中的带斜纹的区域代表暂未使用的内存，新生代(new\_space)被划分为了两个部分，其中一部分叫做inactive new space，表示暂未激活的内存区域，另一部分为激活状态。

## 新生代回收

在V8引擎的内存结构中，新生代主要用于存放存活时间较短的对象。新生代内存是由两个`semispace(半空间)`构成的，内存最大值在`64`位系统和`32`位系统上分别为`32MB`和`16MB`，在新生代的垃圾回收过程中主要采用了`Scavenge`算法。

### 引用计数

引用计数主要是IE等旧浏览器在使用，通过计数器分析变量的引用次数，清除没有引用到的变量。对于存在循环引用的情况则无法处理。比如

```javascript
function cycle() {
    var o1 = {}
    var o2 = {}
    o1.a = o2
    o2.a = o1
}

cycle()
```

其中 o1 引用了 o2，o2 引用了 o1，在cycle函数执行完 o1，o2 都没有再次引用到，但是引用计数算法判断两者都存在引用。

### scavenge 算法

用于V8中的新生代内存，将新生代内存一分为二：From 和 To，在From 和 To 之间转换的过程完成垃圾回收。

![](https://3490195898-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnQxDcxCKODvYvTUWe3%2F-M2H-tKMC5bOnw84GMLl%2F-M2HO9qIfzlGomUQTWTE%2Fimage.png?alt=media\&token=e12393cc-afe3-4386-9082-62576b8b784c)

## 老生代回收

在老生代中，因为管理着大量的存活对象，如果依旧使用`Scavenge`算法的话，很明显会浪费一半的内存，因此已经不再使用`Scavenge`算法，而是采用新的算法`Mark-Sweep(标记清除)`和`Mark-Compact(标记整理)`来进行管理。

{% hint style="info" %}
`引用计数`的算法，两个变量均存在指向自身的引用，因此依旧无法被回收，导致内存泄漏。

因此为了避免循环引用导致的内存泄漏问题，截至2012年所有的现代浏览器均放弃了这种算法，转而采用新的`Mark-Sweep(标记清除)`和`Mark-Compact(标记整理)`算法
{% endhint %}

### 标记清除

早期V8中堆内存采用的一种清除算法，全局扫描堆内存找出未使用到的对象进行标记并清除，由于未进行内存整理会存在内存碎片。

![](https://3490195898-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnQxDcxCKODvYvTUWe3%2F-M2H-tKMC5bOnw84GMLl%2F-M2HQ59wH6nsm2I11rfq%2Fimage.png?alt=media\&token=833e3f10-5055-4031-a80e-7c0bc4f73a1e)

## 避免内存泄露

1. 尽可能少的创建全局变量
2. 手动清除定时器
3. 少用闭包
4. 清除DOM引用
5. 使用弱引用
