一、G1(Garbage First)
G1GettingStarted:出现Young GC、Full GC、Old Generation GC、Mixed,未出现Minor、Major等词。
-verbosegc (which is equivalent to -XX:+PrintGC 也有说不一样 未确认) sets the detail level of the log to fine.
GC类型:Young GC(Minor GC)、Mixed GC、Full GC,各GC类型的触发条件
可达性分析算法
1.1、G1概要
G1的主要设计目标是延迟可控的情况下尽可能高的吞吐量。
分代:新生代(Eden、Survivor)、老年代(Old)
分区:堆分为多个分区,每个分区一种角色(Eden、Survivor、Old)
一般来说(还有其他因素影响):
eden区内存不足,就会执行Young GC;
老年代内存不足,元数据空间内存不足,就会执行Full GC
G1的堆被划分为一组大小相等的堆区域,每个区域都是一段连续的虚拟内存。,每个区域关联一种角色(Eden, survivor, old)。
区域大小由 JVM 在启动时选择,JVM 通常设定 2000 个区域,大小从 1 到 32Mb 不等。
这些区域被映射到伊甸园Eden、幸存者Survivor和老一代空间Old的逻辑表示中。
还有一种特殊区域是巨型对象空间Humongous,在若干个连续的区域,一般是分配在Old区。
关键特性:
分区式垃圾回收:将堆空间划分为多个固定大小的区域(Region),每个区域可以是新生代(Eden or Survivor)或老生代。这种分区式的设计使得垃圾回收可以针对特定的区域进行,而不是整个堆空间。
并发标记:在后台线程中执行并发标记(Concurrent Marking),以确定哪些对象是活跃的。这个过程与应用程序的执行并行进行,几乎不影响应用程序的性能。
混合回收:通过混合回收(Mixed GC)来回收新生代和老生代中的垃圾。混合回收首先会回收新生代区域,然后选择一些老生代区域进行回收。这样可以在不影响应用程序性能的前提下,逐步清理老生代区域中的垃圾。
垃圾优先级:根据每个区域的垃圾填充情况来确定回收的优先级。这种策略被称为“垃圾优先”(Garbage-First),因为 G1 会优先回收那些包含更多垃圾的区域。
可预测的停顿时间:设计目标之一是提供可预测的停顿时间。通过分区式垃圾回收和混合回收,G1 可以将大部分的垃圾回收工作转移到后台线程中,从而减少应用程序的停顿时间。
动态调整:G1可以根据实际的垃圾回收情况动态调整分区的大小和数量,以适应不同的工作负载。
总的来说,G1是一种高效、可扩展的垃圾回收器,适用于大型、实时性要求高的 Java 应用程序。它通过并发标记、混合回收和垃圾优先级等技术,提供了更好的性能和可预测性。
1.2、G1的数据结构
RSets(全称Remembered Sets)、card table、CSets
RSets是一个RSet Bitmap数组,每个区域有一个对应的RSet Bitmap位图。一个RSet Bitmap的每一位代表了该区域中的一个对象,1表示该对象被其他区域引用。
card table
RSet的实现中有一个重要的组成部分,称为“card table”。Card table实际上是一个位图(Bitmap),它将堆空间划分为固定大小的“卡片”(Card),每个卡片通常为512字节。每个卡片对应着位图中的一个位,如果某个卡片中包含了指向其他区域对象的引用,那么相应的位将被设置为1。
当一个对象被分配到某个卡片中,并且它指向了其他区域的对象时,G1会将该卡片对应的位设置为1。这样,G1就可以快速地确定哪些卡片中存在跨区域的引用。
在并发标记阶段,G1会遍历整个堆空间,并根据卡片表中的信息,标记出所有可达的对象。然后,在后续的垃圾收集阶段,G1会根据RSet的信息,逐个处理每个包含存活对象的区域。
总之,card table是RSet的重要组成部分,它帮助G1高效地跟踪跨区域的对象引用关系,并在垃圾收集过程中正确地处理和回收对象。
Card table和RSet Bitmap是两个相关但不同的概念。
RSet Bitmap(记录被引用)的每个位表示了该Region中一个特定的对象是否被其他Region的对象引用。
Card table(记录引用)表示卡片中是否包含了指向其他区域对象的引用。
CSets(全称Collection Sets)
CSets的主要作用是确定在垃圾收集过程中,哪些区域将被处理和回收。
对象是否可达的Marking Bitmap结构
在G1执行并发标记(Concurrent Marking)过程时,首先从GCRoot开始遍历,然后标记所有可达的对象。标记的结果通常会被记录在一个称为Marking Bitmap的数据结构中。
Marking Bitmap是一种位图(Bitmap),其中的每一位对应着堆中的一个对象。如果某一位被设置为1,表示该对象已被标记为可达;如果某一位被设置为0,表示该对象尚未被标记,可能是不可达的。
Marking Bitmap是存储在堆外的一种数据结构。它不占用Java堆的空间,而是使用本地内存来存储。
对象的年龄
- 对象的年龄信息通常保存在对象头(Object Header)中。
对象头是JVM为每个对象分配的一小段内存,用于存储对象的元数据,例如:对象的哈希码、锁状态(用于同步)、GC分代年龄(即对象的年龄)。
在HotSpot JVM中,对象的年龄通常用一个4位的字段来存储,因此最大年龄为15(默认晋升阈值)。 - 当一个对象被创建并分配到 Eden Region 时,它的初始年龄值为0。
- 对象年龄的增加规则
每次对象在Young GC中存活并从一个区域(Eden或Survivor)复制到另一个区域(Survivor)时,其年龄会增加1。
例如:
对象在Eden Region中创建,年龄为0。
第一次Young GC后,对象被复制到Survivor Region,年龄变为1。
第二次Young GC后,对象被复制到另一个Survivor Region,年龄变为2。
当对象的年龄达到晋升阈值(默认是15)时,它会被晋升到老年代。 - 晋升阈值
晋升阈值可以通过JVM参数 -XX:MaxTenuringThreshold 进行配置,默认值为15。
如果Survivor区空间不足,或者对象的大小超过一定限制,对象可能会提前晋升到老年代,即使其年龄未达到阈值。
1.3、G1的GC类型或GC事件(或方式?)
Young GC(Minor GC)
Mixed GC
Full GC(覆盖新生代、老年代以及元数据空间的垃圾收集)
Major GC(据说与FullGC不同,只回收老生代不回收新生代,待确认)
部分收集(Partial GC),指其他不完整地收集整个Java堆的垃圾收集,除Full GC之外的其他GC。
触发条件
Young GC(Minor GC):当Eden区满了时触发。
Major GC:老年代满时触发,通常伴随新生代GC一起发生。
Mixed GC:当老年代占据堆内存一定阈值时触发(据说45%,待确认)。
Full GC:由系统调用或内存占用较多时自动触发。
Stop-The-World,简称STW,停止所有其他线程,只执行GC线程。
我的理解:所有GC回收不管是新生代还是老生代,回收前都基于标记,Full GC阶段本身没有标记,但也基于其他阶段如Young GC或Mixed GC的标记;
都基于垃圾标记,Young GC一般选择部分新生代区域进行回收(不是所有新生代区域);
YoungGC
YoungGC执行步骤
初始标记:标记所有直接可达的对象。短暂的停顿STW。
并发标记:在后台线程中继续标记所有可达的对象。不停止用户线程。
最终标记:处理并发标记阶段中未能标记的对象。短暂的停顿STW。
清除垃圾:计算每个新生代区域中存活的对象数量,并根据这些信息来选择要回收的区域。然后,G1会在一个停顿中将存活的对象从选择的区域中移动到新的空闲区域。
Mixed GC
Mixed GC是G1特有的一个GC事件,它不仅回收新生代区域中的对象,还会选择一些老生代区域进行回收。Mixed GC的标记清除过程与YoungGC类似,但有以下几点不同:
并发标记的范围扩大:在Mixed GC中,G1会对整个堆空间(包括新生代和老生代)进行并发标记,而不仅仅是新生代区域。
选择老生代区域:在清除垃圾阶段,G1会根据老生代区域的垃圾填充情况选择一些区域进行回收。
停顿时间可能更长:由于Mixed GC涉及到更多的区域和对象,停顿时间可能比YoungGC更长。
Full GC
Full GC是对整个堆空间进行垃圾回收的过程。
Full GC通常是由以下情况之一触发的:
a) 没有足够的空间来存储新的对象;
b) 并发标记和混合回收无法及时清理垃圾。并发标记本身也
单线程执行:与并发执行的YoungGC和Mixed GC不同,Full GC通常是单线程执行的。
没有并发标记:在Full GC中,G1不会执行并发标记,而是直接进行标记清除。并发标记通常在Full GC之前(Young GC、Mixed GC)就已经完成了,因为Full GC需要依赖于并发标记的结果来决定哪些对象是存活的。
停顿时间通常较长:由于Full GC需要遍历整个堆空间,停顿时间通常比YoungGC和Mixed GC都要长。