Skip to content

Latest commit

 

History

History
126 lines (80 loc) · 4.71 KB

File metadata and controls

126 lines (80 loc) · 4.71 KB

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 默认内存限制。避免执行过程内存用的过多导致崩溃

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 空间之间进行复制。

型的牺牲空间换取时间的算法,内存中有一半空间始终是空闲的,新生代中对象的生命周期较短,恰恰适合这个算法。

  1. 老生代标记删除整理
  • 标记未使用的空间
  • 删除标记的空间
  • 删除后内存空间会产生空隙,需要进行整理使空间连续(数组需要连续的空间存储)

Mark Sweep:

Mark Sweep 是将需要被回收的对象进行标记,在垃圾回收运行时直接释放相应的地址空间

Alt MarkSweep

Mark Compact:

Mark Compact 的思想有点像新生代垃圾回收时采取的 Cheney 算法:将存活的对象移动到一边,将需要被回收的对象移动到另一边,然后对需要被回收的对象区域进行整体的垃圾回收。

Alt MarkCompact

新生代晋升到老生带

实际使用的堆内存是新生代的两个 semispace 空间大小和老生代所用内存大小之和。当一个对象经过多次复制依然存活时,它将会被认为是生命周期较长的对象。这种较长生命周期的对象随后会被移动到老生代中,采用新的算法进行管理。对象从新生代中移动到老生代中的过程称为 晋升

对象晋升的条件主要有两个,一个是对象是否经历过 Scavenge 回收,一个是 To 空间的内存占用比超过限制。

  1. 变量是否经历过回收?
  • 是,进入老生代
  • 否,进入 To 空间
  1. To 空间是否已经使用 25%(8MB)?
  • 是,进入老生代
  • 否,进入 To 空间

V8 如何处理变量

使用 node 查看内存使用情况

  • process.memoryUsage()
// 单位 byte
{
   rss: 23273472,
  heapTotal: 9682944,
  heapUsed: 5391304,
  external: 8874
}

变量

  • 内存主要就是存储变量等数据的

JS 声明变量并赋值时,所使用对象的内存就分配在堆中。如果已申请的堆空闲内存不够分配新的对象,将继续申请堆内存,直到对的大小超过 V8 的限制为止。

变量分为局部变量和全局变量,这两者的回收方式有差异

  • 局部变量当程序执行结束,且没有引用的时候就会随着消失
    • 局部变量会回收,只是说可以回收,并不是用完立即回收
  • 全局对象会始终存活到程序运行结束

内存溢出例子

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