图解 Google V8 # 20 :垃圾回收(一):V8的两个垃圾回收器是如何工作的?

简介: 图解 Google V8 # 20 :垃圾回收(一):V8的两个垃圾回收器是如何工作的?

说明

图解 Google V8 学习笔记



垃圾数据是怎么产生的?

例子:

window.test = new Object()
window.test.a = new Uint16Array(100)


上面代码内存布局图:

55672ae94dff43bba6afe1efa2ff320f.png


在上面的基础上,添加代码执行:

window.test.a = new Object()


此时的内存布局:a 属性之前是指向堆中数组对象的,现在已经指向了另外一个空对象,此时堆中的数组对象就成为了垃圾数据。


314ce78440b54fae928322f2bc7ca294.png



V8 虚拟机是怎么实现垃圾回收的?



垃圾回收算法


目前 V8 采用的可访问性(reachability)算法来判断堆中的对象是否是活动对象。具体地讲,这个算法是将一些 GC Root 作为初始存活的对象的集合,从 GC Roots 对象出发,遍历 GC Root 中的所有对象:


   通过 GC Root 遍历到的对象,该对象是可访问的(reachable),那么必须保证这些对象应该在内存中保留,称可访问的对象为活动对象;


   通过 GC Roots 没有遍历到的对象,则是不可访问的(unreachable),那么这些不可访问的对象就可能被回收,称不可访问的对象为非活动对象。



什么是 GC Roots?


在浏览器环境中,GC Root 有很多,通常包括了以下几种 (但是不止于这几种):


   全局的 window 对象(位于每个 iframe 中);


   文档 DOM 树,由可以通过遍历文档到达的所有原生 DOM 节点组成;


   存放栈上变量。


垃圾回收是怎么实现?


一般来说,频繁回收对象后,内存中就会存在大量不连续空间,这些不连续的内存空间称为内存碎片。

垃圾回收的大致流程:


   通过 GC Root 标记空间中活动对象和非活动对象。

   回收非活动对象所占据的内存:就是在所有的标记完成之后,统一清理内存中所有被标记为可回收的对象。

   做内存整理:当内存中出现了大量的内存碎片之后,如果需要分配较大的连续内存时,就有可能出现内存不足的情况,所以最后一步需要整理这些内存碎片。


代际假说 The Generational Hypothesis


代际假说是垃圾回收领域中一个重要的术语,特点:


   大部分对象在内存中存活的时间很短:比如函数内部声明的变量,或者块级作用域中的变量,当函数或者代码块执行结束时,作用域中定义的变量就会被销毁。因此这一类对象一经分配内存,很快就变得不可访问;


   不死的对象,会活得更久:比如全局的 window、DOM、Web API 等对象。


两个垃圾回收器


V8 的垃圾回收策略,就是建立在代际假说的基础之上的。


在 V8 中,会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对象,新生代通常只支持 1~8M 的容量,老生代中存放生存时间久的对象。


   主垃圾回收器 -Major GC:主要负责老生代的垃圾回收。


   副垃圾回收器 -Minor GC (Scavenger):主要负责新生代的垃圾回收。



副垃圾回收器


新生代中的垃圾数据用 Scavenge 算法来处理。


Scavenge 算法


所谓 Scavenge 算法,是把新生代空间对半划分为两个区域,一半是对象区域 (from-space),一半是空闲区域 (to-space),如下图所示:

d36bf48e97af4bfeb08a3e4e27686e86.png


垃圾回收过程:


  1. 首先要对对象区域中的垃圾做标记;
  2. 标记完成之后,就进入垃圾清理阶段。
  3. 副垃圾回收器会把这些存活的对象复制到空闲区域中,同时它还会把这些对象有序地排列起来。
  4. 完成复制后,对象区域与空闲区域进行角色翻转。


f7aea17901344f688d43156f1248c052.png


角色翻转示意图:

007d6e92e4384d4fa94807898193d652.png


新加入的对象都会存放到对象区域,当对象区域快被写满时,就需要执行一次垃圾清理操作。副垃圾回收器会采用对象晋升策略,移动那些经过两次垃圾回收依然还存活的对象到老生代中。



主垃圾回收器


主垃圾回收器是采用标记 - 清除(Mark-Sweep)的算法进行垃圾回收的。


老生代中的对象除了新生代中晋升的对象,还有一些大的对象会直接被分配到老生代里。


两个特点:


   对象占用空间大

   对象存活时间长


标记 - 清除(Mark-Sweep)


工作过程:


   标记过程阶段:标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。


   垃圾的清除过程:主垃圾回收器会直接将标记为垃圾的数据清理掉。


标记清除过程示意图:


615e99d603ce45ca8515b68d28c4db75.png



标记 - 整理(Mark-Compact)


对一块内存多次执行标记 - 清除算法后,会产生大量不连续的内存碎片。而碎片过多会导致大对象无法分配到足够的连续内存,于是又引入了另外一种算法——标记 - 整理(Mark-Compact)。


工作过程:


   先标记可回收对象:这个算法的标记过程仍然与标记 - 清除算法里的是一样的

   让所有存活的对象都向一端移动,然后直接清理掉这一端之外的内存。


标记整理过程示意图:

ed2d89444ae64ac3996c55d70595c3ba.png



目录
相关文章
|
4月前
|
缓存 监控 算法
Java面试题:描述Java垃圾回收的基本原理,以及如何通过代码优化来协助垃圾回收器的工作
Java面试题:描述Java垃圾回收的基本原理,以及如何通过代码优化来协助垃圾回收器的工作
87 8
|
5月前
|
存储 Java 程序员
V8垃圾回收?看这篇就够了!
V8垃圾回收?看这篇就够了!
|
5月前
|
算法 Java 云计算
JVM垃圾回收的历史演进:从GC算法到垃圾回收器选择
JVM垃圾回收的历史演进:从GC算法到垃圾回收器选择
|
6月前
|
存储 监控 算法
JVM工作原理与实战(二十七):堆的垃圾回收-G1垃圾回收器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了G1垃圾回收器、G1垃圾回收器的回收方式、G1垃圾回收器执行流程、垃圾回收器的选择等内容。
106 0
|
6月前
|
机器学习/深度学习 监控 算法
JVM工作原理与实战(二十六):堆的垃圾回收-垃圾回收器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了垃圾回收器、Serial垃圾回收器、SerialOld垃圾回收器、ParNew垃圾回收器、CMS垃圾回收器、Parallel Scavenge垃圾回收器、Parallel Old垃圾回收器等内容。
62 0
|
存储 算法 Java
JVM学习日志(十三) G1垃圾回收流程 及 垃圾回收器总结
G1垃圾回收流程 及 垃圾回收器 总结 简述
204 0
JVM学习日志(十三) G1垃圾回收流程 及 垃圾回收器总结
|
存储 算法 JavaScript
V8如何进行垃圾回收的
V8如何进行垃圾回收的
39 0
|
存储 Web App开发 监控
Js中的垃圾回收及V8引擎的优化
Js中的垃圾回收及V8引擎的优化
288 0
JVM垃圾回收器详解:串行回收新生代内存管理垃圾回收的触发机制
在讨论新生代垃圾回收之前,首先要解决的问题就是:谁能触发垃圾回收?何时触发垃圾回收? 从垃圾回收的角度来说,既可以进行主动回收,也可以进行被动回收。
|
缓存 JavaScript 前端开发
图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)
图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)
382 0
图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)

热门文章

最新文章