垃圾回收机制优化-全停顿及相关优化

大噶好,上次我们说到新老垃圾回收机制的不同,说到老生代做了一些东西,像标记清除和标记整理,为了处理 GC 和 JS 应用逻辑之间的一些纠葛,我们做了一些举措和一些优化。

分开 JS 应用逻辑和 GC 逻辑的神 – 全停顿

我们来假想一个场景,当我们有大量的内存,在标记删除之后,留下了大面积的内存碎片,由于碎片太多了,标记整理必然不会很快。

这时候就出现了一个问题,JS 应用逻辑和垃圾回收器的内存资源竞争的时候,该怎么处理?

答:垃圾回收器会将 JS 应用暂停,这个过程被称为全停顿

想一下,垃圾回收器一旦将 JS 应用程序暂停了,如果页面还没来得及渲染完成,现在是不是卡得一批?

那么很自然,我们就要想办法去做优化,接下来我们来聊聊优化方式。

优化方式1-增量标记

增量标记是指在进行垃圾回收时,只对部分存活对象进行标记,而不是对所有对象都进行标记。这样可以减少垃圾回收的停顿时间,提高系统的响应速度。

v8的增量标记大致流程如下:

  1. 初始标记(GC开始执行时):遍历所有根对象,标记它们及其直接引用的对象为存活对象。这个阶段会停止应用程序执行,直到标记完成。

  2. 并发标记(在应用程序执行期间):遍历标记存活对象的引用,标记被引用的对象为存活对象。这个阶段会和应用程序执行交替进行,不会停止应用程序执行。

  3. 完整标记(在应用程序执行期间):在应用程序执行时,检查是否有新的存活对象被创建,并标记它们的引用。这个阶段也会和应用程序执行交替进行,不会停止应用程序执行。

  4. 清除(GC结束时):清除所有没有标记的对象,释放它们占用的内存空间。这个阶段会停止应用程序执行。

由于增量标记是在应用程序执行期间进行的,所以可以最大限度地减少垃圾回收的停顿时间。但是,由于标记过程要和应用程序执行交替进行,所以增量标记可能会导致应用程序的吞吐量略微降低。

流程类似于:JS -> GC -> JS -> GC -> JS -> xxxxxx

优化方式2-懒性清理

V8的懒性清理,也称为惰性清理(Lazy Sweeping),是一种垃圾回收机制,用于延迟清理未标记对象所占用的内存空间,以减少垃圾回收期间的停顿时间。

具体来说,V8在进行增量标记时,只会标记存活对象,而未标记的对象则被视为垃圾。

一般情况下,当增量标记结束后,V8会立即清理未标记对象所占用的内存空间。

但是,为了避免清理操作导致的大量CPU使用和系统停顿,在某些情况下,V8会采用惰性清理机制。

具体来说,V8会等待一段时间,以期望在这段时间内应用程序会继续创建新的对象和进行垃圾回收,从而让存活对象在内存中重新排列。

在此期间,暂时不会执行扫描和清理操作。只有当V8认为已经到了清理的最佳时机时,才会启动扫描和清理操作。这样可以避免对垃圾回收期间的应用程序性能造成过大的影响。

假如当前的可用内存足以让我们快速的执行代码,其实我们是没必要立即清理内存的,可以将清理的过程延迟一下,让JavaScript逻辑代码先执行,也无需一次性清理完所有非活动对象内存,垃圾回收器会按需逐一进行清理,直到所有的页都清理完毕。

优化方式3-并发

V8中垃圾回收的并发是指在应用程序执行期间,同时进行垃圾回收和应用程序执行操作,以减少垃圾回收所造成的应用程序停顿时间,提高系统的响应速度。具体来说,在V8中,并发的垃圾回收主要包括以下几个方面:

  1. 并发标记和清除:在中断应用程序执行前后,V8会启动并发标记和清除。并发标记阶段会标记存活对象,而并发清除阶段则会回收垃圾对象所占用的内存空间。通过这种方式,V8可以在应用程序继续执行的同时,进行并发的垃圾回收,从而减少停顿时间。

  2. 惰性清理:除了并发标记和清除之外,在某些情况下,V8还会采用惰性清理机制,即等待一段时间以后再对未标记对象所占用的内存空间进行清理。这种方式可以在运行时间不够长或者生成的垃圾量比较少时,尽可能减少垃圾回收的停顿时间。

优化方式4-并行

并行式GC允许主线程和辅助线程同时执行同样的GC工作,这样可以让辅助线程来分担主线程的GC工作,使得垃圾回收所耗费的时间等于总时间除以参与的线程数量(加上一些同步开销)。

垃圾回收机制优化-全停顿及相关优化

参考文章:
深入理解谷歌最强V8垃圾回收机制

原文链接:https://juejin.cn/post/7238997635755049020 作者:阳树阳树

(0)
上一篇 2023年5月31日 上午10:15
下一篇 2023年5月31日 上午10:25

相关推荐

发表评论

登录后才能评论