V8的垃圾回收机制

32 篇文章 4 订阅

V8内存限制

在 JavaScript 中,V8 只能使用系统的一部分内存

具体来说,在 64 位系统中,V8 最多只能分配 1.4g,在 32 位系统中,最多只能分配 0.7g。

在浏览器端,大内存的需求并不多,但是对于后端而已,node.js如果遇到一个 2g 多的文件,那么将无法将其全部读入内存并进行各种操作。

JS 的内存分为栈内存和堆内存。

栈内存很简单,当 ESP 指针下移,也就是上下文切换之后,栈顶的空间会被自动回收。

但是堆内存就相对比较复杂,下面会着重分析。

我们知道,所有对象类型的数据在 JS 中都是通过堆进行空间存储的。当我们创建一个新对象时,会自动分配一段内存在堆上。你可以一直新建对象,让 V8 为它分配空间,直至堆的大小达到上限

为什么 JS 要设置内存上限呢?

现在主流的机器都有 8g 以上了。原因有二:JS 单线程的执行机制,JS 的垃圾回收机制

首先,JS 是单线程执行的,这意味着一旦进入到垃圾回收,那么其他的代码块运行都会暂停

其次,垃圾回收是非常消耗时间的操作。

V8 官方是这样描述的:

以 1.5g 的垃圾回收堆内存为例,V8 做一次小的垃圾回收需要 50ms 以上,做一次非增量式的垃圾回收甚至需要 1s 以上。

而在这么长的时间中,JS 的其他代码会一直暂停,造成应用卡顿,应用的性能和响应能力直线下降。

因此,V8 选择简单粗暴的方式:直接限制堆内存的大小

新生代内存与老生代内存

V8 把堆内存分成了两部分进行处理:

  • 新生代内存:临时分配的内存,存活时间短
  • 老生代内存:常驻内存,存活时间长

二者占比:

image

根据两种场景,V8 使用了不同的垃圾回收策略,进行针对性优化。

新生代内存

新生代内存的内存默认限制非常小,在 64 位系统中是 32MB,在 32 位系统中是 16MB。

这是因为新生代内存的变量存活时间短,来了就走,不容易产生大的内存负担。

那么新生代的垃圾回收是怎么运行的呢?

首先将新生代内存空间一分为二

image

其中 From 表示正在使用的内存,To 表示目前闲置的内存。

当进行垃圾回收时,V8 会把 From 部分的对象检查一遍,如果存活则复制到 To 内存中,并在 To 内存中按照顺序从头放置;如果是非存活的对象则直接回收掉

当 From 内存中的所有存活对象都放入了 To 内存中之后From 和 To 角色互换,原本的 From 变成 To,原本的 To 变成 From,如此循环。

为什么不直接把非存活的对象直接回收,而要搞这么多操作?

注意:刚才说到,在 To 内存中按照顺序从头放置,这是为了应对这样的场景:

image

深色代表存活对象,白色表示空闲内存。由于堆内存是连续分配的,这样零零散散的空间可能会造成大的对象无法分配内存。这种零散的空间叫做内存碎片,而新生代内存的垃圾回收算法也称为 Scavenge 算法。

Scavenge 算法主要就是解决内存碎片的问题,在一顿复制之后,To 空间变成这样:

image

这样就大大提高了内存的空间利用率。

不过 Scavenge 算法的劣势也非常明显,内存只能使用新生代内存的一半,但是它只存放生命周期短的对象,这样的对象一般很少,因此时间性能非常优秀。

老生代内存

如果新生代中的变量如果经过多次回收之后依然存在,则会被放入老生代内存中,这种现象叫做晋升

发生晋升的原因很多:

  • 已经经历一次 Scavenge 回收
  • To(闲置)空间的内存占用率超过 25%

在老生代的垃圾回收机制中,老生代累计的变量空间一般都是很大的,当然不能用 Scavenge 算法进行处理,否则复制操作将耗费大量的性能,还浪费一半的内存。

老生代内存的垃圾回收机制如下:

第一步,进行标记-清除。主要分成两个阶段——标记阶段和清除阶段。首先会遍历堆中所有的对象,对它们做个上标记,然后对正在使用中的变量被强引用的变量取消标记,剩下的就是要删除的变量,在随后的清除阶段对其进行空间回收。

同理,这也会引发内存碎片的问题。存活对象的空间不连续,会对后面的空间分配造成影响。老生代选择另一种方式进行处理。

第二步,整理内存碎片。V8 的解决方式非常简单直接,在清除阶段结束后,把存活的对象全部往一端靠拢

image

由于是移动对象,它的执行速度不可能很快,事实上也是整个过程中耗费时间最长的部分。

增量标记

由于 JS 单线程的特点,垃圾回收的过程中,不可避免地导致业务代码执行的堵塞。如果老生代的垃圾回收任务很重,那么耗时是非常可怕的,严重影响应用的性能

为了避免这样的问题,V8 采取了增量标记的方案。即将一口气完成的标记任务分成很多个小的部分完成,每完成一个小部分就停一下,让业务逻辑运行一会儿,然后再执行下面的部分。如果循环,直到标记阶段完成后才进行内存碎片的整理

经过增量标记后,垃圾回收机制对 JS 应用的阻塞时间减少到原来的 1/6,是一个非常巨大的进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值