## V8 引擎内存

-   新生代内存空间（64 位 OS 下大小限制为 64MB）
    -   semi space Form
    -   semi space To
-   老生代内存空间（64 位 OS 下大小限制为 1400MB）

### 内存大小

和操作系统有关 64 位 OS 下,内存大小限制为 1.4GB,
新生代内存空间大小限制为 64MB,
老生代内存空间大小限制为 1400MB

由于以下原因，js 内存设计不会太大：

-   内存不会持久化，执行后进行回收；
-   回收内存是，js 暂停执行，
    回收一次 100mb，需要暂停 6ms

#### 内存大小调整

V8 提供选择来调整内存大小的配置，需要在初始化时候配置生效，遇到 Node 无法分配足够内存给 JS 对象的情况，可以用如下办法来放宽 V8 默认内存限制。避免执行过程内存用的过多导致崩溃

```sh
node --max-old-space-size=1700 index.js
node --max-new-space-size=1024 index.js
```

### 垃圾回收算法

在 V8 中，主要将内存分为新生代和老生代，新生代的对象为存活时间较短的对象，老生代的对象为存活时间较长或常驻内存的对象，

1. 新生代简单的说就是复制

新生代内存空间用来存储新产生的变量,变量小，存在时间短

-   变量先存储在 From 空间里，满足一定条件后发生回收
-   将 From 空间中活着的变量复制到 To 空间中
-   然后清空 From 空间
-   下一次发生回收时 From 和 To 对调

这种复制算法即 Cheney 算法：

> Cheney 算法是一种采用复制的方式实现的垃圾回收算法。它将堆内存一分为二，每一部分空间称为 semispace。在这两个 semispace 空间中，只有一个处于使用中，另一个处于闲置状态。处于使用状态的 semispace 空间称为 From 空间，处于闲置状态的空间称为 To 空间。当我们分配对象时，先是在 From 空间中进行分配。当开始进行垃圾回收时，会检查 From 空间中的存活对象，这些存活对象将被复制到 To 空间中，而非存活对象占用的空间将会被释放。完成复制后，From 空间和 To 空间的角色发生兑换。简而言之，在垃圾回收过程中，就是通过将存活对象在两个 semispace 空间之间进行复制。

型的牺牲空间换取时间的算法，内存中有一半空间始终是空闲的,新生代中对象的生命周期较短，恰恰适合这个算法。

2. 老生代标记删除整理

-   标记未使用的空间
-   删除标记的空间
-   删除后内存空间会产生空隙，需要进行整理使空间连续（数组需要连续的空间存储）

Mark Sweep:

Mark Sweep 是将需要被回收的对象进行标记，在垃圾回收运行时直接释放相应的地址空间

![Alt MarkSweep](./img/mark-sweep.webp)

Mark Compact:

Mark Compact 的思想有点像新生代垃圾回收时采取的 Cheney 算法：将存活的对象移动到一边，将需要被回收的对象移动到另一边，然后对需要被回收的对象区域进行整体的垃圾回收。

![Alt MarkCompact](./img/mark-compact.webp)

### 新生代晋升到老生带

实际使用的堆内存是新生代的两个 semispace 空间大小和老生代所用内存大小之和。当一个对象经过多次复制依然存活时，它将会被认为是生命周期较长的对象。这种较长生命周期的对象随后会被移动到老生代中，采用新的算法进行管理。对象从新生代中移动到老生代中的过程称为 **晋升**。

对象晋升的条件主要有两个，一个是对象是否经历过 Scavenge 回收，一个是 To 空间的内存占用比超过限制。

1. 变量是否经历过回收？

-   是，进入老生代
-   否，进入 To 空间

2. To 空间是否已经使用 25%（8MB）？

-   是，进入老生代
-   否，进入 To 空间

### V8 如何处理变量

#### 使用 node 查看内存使用情况

-   process.memoryUsage()

```
// 单位 byte
{
   rss: 23273472,
  heapTotal: 9682944,
  heapUsed: 5391304,
  external: 8874
}
```

#### 变量

-   内存主要就是存储变量等数据的

JS 声明变量并赋值时，所使用对象的内存就分配在堆中。如果已申请的堆空闲内存不够分配新的对象，将继续申请堆内存，直到对的大小超过 V8 的限制为止。

变量分为局部变量和全局变量，这两者的回收方式有差异

-   局部变量当程序执行结束，且没有引用的时候就会随着消失
    -   局部变量会回收，只是说可以回收，并不是用完立即回收
-   全局对象会始终存活到程序运行结束

内存溢出例子

```js
const size = 100 * 1024 * 1024;

let arrobj = {};

for (let i = 0; i < 10; i++) {
    arrobj[i] = new Array(size);
}

console.log("done");
```

参考：
https://github.com/zqjflash/nodejs-memory
https://www.jianshu.com/p/455d0b9ef0a8
