前言
基于标记 - 整理算法,作用与年轻代老年代的垃圾回收器。适用场景:要求尽可能可控 GC 停顿时间;内存占用较大的应用。可以用 -XX:+UseG1GC 使用 G1 收集器,jdk9 默认使用 G1 收集器。与 CMS 相比,G1 有内存整理过程(标记-压缩),避免了内存碎片;STW 时间可控(能预测 GC 停顿时间)
流程
- 1、初始标记(Initial Marking)
标记GC Roots能直接关联到的对象,停顿线程,但耗时很短。 - 2、并发标记(Concurrent Marking)
从 GC Root 开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行。 - 3、最终标记(Final Marking)
修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录 - 4、筛选回收(Live Data Counting and Evacuation)
这里有一个预估,对各个 Region 的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划,与用户程序一起并发执行,暂停用户线程,
内存模型
在 G1 之前的其他收集器进行收集的范围都是整个新生代或者老年代,而 G1 不再是这样。使用 G1 收集器时,Java 堆的内存布局就与其他收集器有很大差别,它将,虽然还保留有新生代和老年代的概念,但
- 去掉了新生代或者老年代的物理划分
- 整个 Java 堆划分为多个大小相等的独立区域(Region),新生代和老年代是一部分 Region (不需要连续)的集合。
- Region 之间的对象引用以及其他收集器中的新生代与老年代之间的对象引用,虚拟机都是使用 Remembered Set 来避免全堆扫描的
特性
-
并行与并发: G1 能充分利用多 CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短 Stop-The-World 停顿的时间,部分其他收集器原本需要停顿 Java 线程执行的 GC 动作,G1 收集器仍然可以通过并发的方式让 Java 程序继续执行。
-
分代收集: 与其他收集器一样,分代概念在 G1 中依然得以保留。虽然 G1 可以不需要其他收集器配合就能独立管理整个 GC 堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次 GC 的旧对象以获取更好的收集效果。
-
空间整合: 与 CMS 的“标记—清理”算法不同,G1 从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着 G1 运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次 GC 。
-
可预测的停顿: 这是 G1 相对于 CMS 的另一大优势,降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时 Java(RTSJ)的垃圾收集器的特征了。
这里为啥不说是优点呢?主要是G1收集器目前在商用层面的案例较少,没有表现出足够的性能优势,随着后续对G1的优化,这一问题应该可以解决,虽然有3个 Stop-The-World 停顿的时间。
参考
深入理解Java虚拟机