- 新生代内存空间(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 中,主要将内存分为新生代和老生代,新生代的对象为存活时间较短的对象,老生代的对象为存活时间较长或常驻内存的对象,
- 新生代简单的说就是复制
新生代内存空间用来存储新产生的变量,变量小,存在时间短
- 变量先存储在 From 空间里,满足一定条件后发生回收
- 将 From 空间中活着的变量复制到 To 空间中
- 然后清空 From 空间
- 下一次发生回收时 From 和 To 对调
这种复制算法即 Cheney 算法:
Cheney 算法是一种采用复制的方式实现的垃圾回收算法。它将堆内存一分为二,每一部分空间称为 semispace。在这两个 semispace 空间中,只有一个处于使用中,另一个处于闲置状态。处于使用状态的 semispace 空间称为 From 空间,处于闲置状态的空间称为 To 空间。当我们分配对象时,先是在 From 空间中进行分配。当开始进行垃圾回收时,会检查 From 空间中的存活对象,这些存活对象将被复制到 To 空间中,而非存活对象占用的空间将会被释放。完成复制后,From 空间和 To 空间的角色发生兑换。简而言之,在垃圾回收过程中,就是通过将存活对象在两个 semispace 空间之间进行复制。
型的牺牲空间换取时间的算法,内存中有一半空间始终是空闲的,新生代中对象的生命周期较短,恰恰适合这个算法。
- 老生代标记删除整理
- 标记未使用的空间
- 删除标记的空间
- 删除后内存空间会产生空隙,需要进行整理使空间连续(数组需要连续的空间存储)
Mark Sweep:
Mark Sweep 是将需要被回收的对象进行标记,在垃圾回收运行时直接释放相应的地址空间
Mark Compact:
Mark Compact 的思想有点像新生代垃圾回收时采取的 Cheney 算法:将存活的对象移动到一边,将需要被回收的对象移动到另一边,然后对需要被回收的对象区域进行整体的垃圾回收。
实际使用的堆内存是新生代的两个 semispace 空间大小和老生代所用内存大小之和。当一个对象经过多次复制依然存活时,它将会被认为是生命周期较长的对象。这种较长生命周期的对象随后会被移动到老生代中,采用新的算法进行管理。对象从新生代中移动到老生代中的过程称为 晋升。
对象晋升的条件主要有两个,一个是对象是否经历过 Scavenge 回收,一个是 To 空间的内存占用比超过限制。
- 变量是否经历过回收?
- 是,进入老生代
- 否,进入 To 空间
- To 空间是否已经使用 25%(8MB)?
- 是,进入老生代
- 否,进入 To 空间
- 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

