当前位置:硬件测评 > 可能是最全的G1学习笔记

可能是最全的G1学习笔记

  • 发布:2023-10-06 17:35

简介

最近遇到很多朋友来咨询G1调音的事情。去年我自己学了G1,但当时只触及了皮毛,所以我也有很多疑问。总的来说,我对G1有几个疑惑,希望这篇文章能够解答。

  1. G1的初衷是什么?
  2. G1适合什么场景使用?
  3. G1 的权衡是什么?
  4. G1的详细流程?
  5. 如何理解G1的gc日志?
  6. G1 调校思路?
  7. G1和CMS的比较与选择?

1。基础知识

1。初衷

在G1提出之前,经典的垃圾收集器主要有三种类型:串行收集器、并行收集器和并发标记清除收集器。这三种收集器可以满足Java应用程序的三种不同需求:最小化内存使用和并发开销、最大化应用程序吞吐量和最小化应用程序GC暂停时间。然而,上述三种垃圾收集器有几个共同的问题:(1)所有针对老年代的操作都必须扫描整个老年代。生成空间; (2)年轻代和年老代是独立的、连续的内存块。首先要确定年轻代和老年代在虚拟地址空间中的位置。

2。设计目标

G1 是服务器端应用程序使用的垃圾收集器。它的目标是在多核、大内存机器上使用。它在大多数情况下可以达到指定的GC暂停时间,同时还可以保持高吞吐量。

3。使用场景

G1 适用于以下应用:

  • 可以像CMS收集器一样,允许垃圾收集线程和应用程序线程并行执行,这需要额外的CPU资源;
  • 压缩可用空间不会延长GC暂停时间;
  • 需要更可预测的GC暂停时间;
  • 无需实现高吞吐量

2。 G1

的重要概念

1。地区

G1采用不同的策略来解决并行、串行和CMS收集器的碎片和停顿时间不可控等问题 - G1将整个堆划分为相同大小的分区(Region),如下所示图中。

每个分区可能属于年轻代,也可能属于老年代,但只能同时属于某一代。
年轻代、幸存者区、老年代的概念仍然存在,并且已经成为逻辑概念,这样更容易复用上一代框架的逻辑。它不需要物理上连续,这带来了额外的好处——有的分区有很多垃圾对象,有的分区有很少的垃圾对象。 G1会优先回收垃圾对象较多的分区,因此可以花费更少。收集这些分区的垃圾需要时间。这就是G1名称的由来,即首先收集垃圾最多的分区。

新一代其实并不适合这个算法。当新生代满时,整个新生代仍然被回收——整个新生代中的对象要么被回收,要么被提升。至于新生代之所以也采用分区机制,是因为它与老年代的策略统一,更容易调整代的大小。

G1 也是压缩收集器。当回收老一代分区时,它将幸存的对象从一个分区复制到另一个可用分区。这个复制过程实现了本地压缩。每个分区的大小从1M到32M不等,但都是2的幂。

2。收藏套装 (CSet)

可回收的分区集合。在 GC 过程中,CSet 中幸存的数据将被移动到另一个可用分区。 CSet 中的分区可以来自 Eden 空间、survivor 空间或老年代。 CSet将占用整个堆空间的不到1%。

3。记忆组 (RSet)

RSet记录了其他Region中的对象引用本Region中对象的关系,属于points-into结构(谁引用了我的对象)。 RSet的值在于,垃圾收集器不需要扫描整个堆来查找谁引用了当前分区中的对象,而只需要扫描RSet即可。

如下图所示,Region1和Region3中的对象都引用了Region2中的对象,因此这两个引用都记录在Region2的RSet中。

摘录大R解释:G1 GC在点出卡表之上增加了一层结构,形成点入RSet:每个区域都会记录哪些其他区域指向它。指针,以及这些指针在哪些牌的范围内。这个RSet实际上是一个哈希表,key是其他区域的起始地址,value是一个集合,里面的元素是卡表的索引。例如,如果区域A的RSet中有一个项目,其键为区域B,并且值包含索引为1234的卡片,则意味着区域B中的卡片引用了区域A。因此对于区域A, RSet记录点入关系;而牌表仍然记录着分出关系。

4。开始快照 (SATB)

SATB是维持并发GC正确性的一种手段。 G1GC的并发理论基础是SATB。 SATB是Taiichi Yuasa为增量标记去除垃圾收集器设计的标记算法。 Yuasa的SATAB的标记优化主要集中在标记-清除垃圾收集器的并发标记阶段。根据R的说法,CMS的增量更新设计要求它在remark阶段重新扫描所有线程堆栈和整个young gen作为root; G1的SATB设计只需要在remark阶段扫描剩余的satb_mark_queue。

SATB 算法创建一个对象图,它是堆的逻辑“快照”。标签数据结构包括两个位图:前一个位图和下一个位图。前一个位图保存了最近完成的标记信息。并发标记周期将创建并更新下一个位图。随着时间的推移,以前的位图会变得越来越过时。最后,在并发标记周期结束时,下一个位图 前一个位图将被覆盖。
下面我们用几个图来描述SATB算法的过程:

  1. 在并发周期开始之前,NTAMS 字段设置为每个分区的当前顶部。并发周期开始后分配的对象将被放置在TAMS之前(图的下半部分),并被明确定义为隐式生存。物体,以及TAMS之后的物体(图上半部分)需要清晰标记。

  2. 并发标记期间的堆分区

  3. 位于堆分区的Bottom和PTAMS之间的对象将被标记并记录在之前的位图中;

  4. 位于堆分区的Top和PATMS之间的对象是隐式生存对象,也记录在前面的位图中;

  5. 在重新标记阶段结束时,所有 NTAMS 之前的对象都将被标记

  6. 并发标记阶段分配的对象将被分配到NTAMS之后的空间,并且它们将作为隐式活动对象记录在下一个位图中。一个并发标记周期完成后,下一个位图将覆盖前一个位图,然后下一个位图将被清除。

SATB 是一种快照标记算法。在并发标记的过程中,垃圾收集器(Collecotr)和应用程序(Mutator)处于活动状态。如果一个对象还没有被标记,此时Mutator就会修改它。参考,那么此时得到的快照是不完整的。如何解决这个问题呢? G1 GC使用SATB写屏障来解决这个问题——在并发标记过程中,对象的旧引用被记录在SATB日志对或缓冲区中。我翻阅了G1的代码,发现实际代码如下——只有对象入队,整个修改过程并没有在写屏障之间完成。

 // 热点/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp
// 这表明我们不需要访问任何 BarrierSet 数据
// 结构体,因此可以从静态上下文中调用它。
模板  static void write_ref_field_pre_static(T* field, oop newVal) {
T heap_oop = oopDesc::load_heap_oop(field);
if (!oopDesc::is_null(heap_oop)) {
入队(oopDesc::decode_heap_oop(heap_oop));
}
}

enqueue的真正代码在hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp,其中使用了JavaThread::satb_mark_queue_set() .is_active()判断是否处于并发标记周期。

void G1SATBCardTableModRefBS::enqueue(oop pre_val) {
// 空值应该已经被过滤掉了。断言(pre_val->is_oop(true),“错误”);
if (!JavaThread::satb_mark_queue_set().is_active()) 返回;
线程* thr = 线程::current();
if (thr->is_Java_thread()) {
JavaThread* jt = (JavaThread*)thr;
//将旧值放入队列
jt->satb_mark_queue().enqueue(pre_val);
} 别的 {
MutexLockerEx x(Shared_SATB_Q_lock, Mutex::_no_safepoint_check_flag);
JavaThread::satb_mark_queue_set().shared_satb_queue()->enqueue(pre_val);
}
}

stab_mark_queue.enqueue 方法首先尝试将先前的值记录在缓冲区中。如果缓冲区已满,则当前的SATB缓冲区将被“退休”并放入全局列表中,然后交给线程。分配新的 SATB 缓冲区。并发标记线程定期检查并处理“已填充”的缓冲区。

3。 G1流程

1。四次操作

G1收集器的收集活动主要包括四个操作:

  • 新一代垃圾收集
  • 后台采集、并发周期
  • 混合垃圾收集
  • 必要时进行完整GC

首先新生代垃圾回收示意图如下:

  • 当Eden区耗尽时,会触发新生代回收。新生代垃圾回收会回收整个新生代
  • 新生代垃圾回收期间,整个应用程序STW
  • 新生代垃圾收集由多个线程并发执行
  • 新生代收集完成后仍然存活的对象将被复制到新的Survivor分区或老年代。

G1设计了一个标记阈值,它描述了占整个Java堆大小的百分比。默认值为 45。可以通过命令 -XX:InitiatingHeapOccupancyPercent(IHOP) 调整该值。一旦达到该阈值,就会触发并发收集周期。注意:这里的百分比是占整个堆大小的百分比,而CMS中的CMSInitiatingOccupancyFraction命令选择的是老年代的百分比。并发收集周期的示例如下:

上图中有几个地方需要注意:

  • 新生代的空间占用发生了变化——在并发收集周期中,至少有一次(也可能有多次)新生代垃圾回收;
  • 请注意,某些分区被标记为
  • 标记周期结束后,老年代占用的空间会变多。这是因为在标记周期期间,新生代的垃圾回收会将对象提升到老年代,而在标记周期期间老年代中不会有任何对象。目的。

其次,G1的并发标记周期包含多个阶段:
并发标记周期使用的算法是我们前面提到的SATB标记算法,输出是找到一些垃圾对象最多的老年代分区。

  • 初始标记(initial-mark)。在此阶段,申请将通过 STW。通常初始标记阶段会与新一代收集一起进行。换句话说——由于两个阶段都需要暂停应用程序,G1 GC 会重用新生代集合来完成初始标记工作。在年轻代垃圾收集期间执行初始标记会使暂停时间稍长并增加CPU开销。初始标记的工作是设置两个 TAMS 变量(NTAMS 和 PTAMS)的值。在此并发周期内,TAMS之上的所有对象都将被识别为隐式存活对象;
  • 根分区扫描(根区域扫描)。此过程不需要暂停应用程序。在初始标记或新生代收集期间复制到幸存者分区的对象需要被视为根。在这个阶段,G1开始扫描。 Survivor分区,survivor分区引用的所有对象都会被扫描并标记。幸存者分区是根分区。因此,现阶段无法进行新一代收集。如果在扫描根分区时恰好新生代空间耗尽,则新生代垃圾回收必须等待根分区扫描结束完成。如果发现根分区扫描和新生代收集的日志在日志中交替出现,则说明当前应用需要调优。
  • 并发标记阶段(concurrent-mark),并发标记阶段是多线程的。我们可以通过-XX:ConcGCThreads来设置并发线程数。默认情况下,G1垃圾收集器会将线程总数设置为并行垃圾线程数的四分之一(-XX:ParallelGCThreads);并发标记会使用trace算法找到所有存活的对象并将它们记录在位图中,因为在TAMS之上的对象被认为是隐式存活的,所以我们只需要遍历TAMS下面的那些;记录标记时发生的参考变化。 SATB的思想是一开始就设置一个快照,然后假设这个快照不发生变化,根据这个快照进行trace。此时,如果某个对象的引用发生变化,则需要通过预写屏障日志将该对象的旧值记录在 SATB 缓冲区中。如果缓冲区已满,则将其添加到全局列表中 - G1 将具有并发标记线程来定期处理此全局列表。
  • 重新标记阶段(remarking),重新标记阶段是最后一个标记阶段,需要暂停整个应用程序。 G1 垃圾收集器将处理剩余的 SATB 日志缓冲区和所有更新的引用。同时,G1垃圾收集器也会发现所有未标记的存活对象。此阶段还将负责参考处理等工作。
  • 清理阶段(cleanup),清理阶段实际回收的内存很小。到这个阶段,G1垃圾收集器主要标记哪些老年代分区可以被回收,并根据其活跃度将老年代从老年代中删除。到一个大数组。这个过程还会做几件事:识别所有空闲分区、RSet梳理、从元空间卸载未使用的类、回收巨型对象等。识别每个分区中幸存对象的好处是,当遇到完全空闲的分区时,它的RSet可以立即清理,分区可以立即回收并释放到空闲队列中,无需放回去。混合收集阶段进入CSet等待回收;梳理 RSet 可以帮助找到无用的引用。

第三,混合回收只会回收部分老年代分区。下图是第一次混合收集前后的堆情况对比。

混合收集将执行多次,直到(几乎)所有标记点老年代分区都被回收,之后它将恢复到常规的新生代垃圾收集周期。当整个堆的使用率超过指定的百分比时,G1 GC将开始新一轮的并发标记周期。在混合收集周期中,对于要回收的分区,该分区中幸存的数据将被复制到另一个分区中。这就是为什么G1收集器的碎片频率比CMS收集器小很多的原因。这种方式回收对象,实际上伴随着当前分区的压缩。

2。两种模式

G1采集器主要有两种模式:

  • Young GC(新生代垃圾回收)
  • Mixed GC(混合垃圾回收)

在R的帖子中,给出了一个假想的G1垃圾回收操作流程,如下图所示。结合上一节的细节,可以清楚地了解G1 GC的正常流程。

3。巨型物体的管理

巨型对象:在G1中,如果对象的大小超过分区大小的一半,则该对象被定义为巨型对象。巨对象直接分配到老年代分区。如果一个对象的大小超过了一个分区的大小,那么会直接在老年代中分配两个连续的分区来存储这个巨对象。巨型分区必须是连续的,并且在分配后不能移动 - 没有任何好处。

由于巨型对象的存在,G1堆中的分区分为新生代分区、老年代分区和巨型分区三种,如下图所示:

如果一个巨型对象跨越两个分区,第一个分区称为“起始巨型”,后续分区称为“连续巨型”,这样最后一个分区的部分空间就被浪费了。如果有很多巨型对象只是比分区大小稍大一点,就会造成大量的空间浪费,导致堆碎片。如果发现巨对象分配导致连续多次并发循环,堆出现碎片(明明空间足够,却触发FULL GC),可以考虑调整-XX:G1HeapRegionSize参数来减少或消除巨型对象的分配。

关于巨对象的回收:在JDK8u40之前,巨对象的回收只能在并发收集周期的清理阶段或者FULL GC过程中进行回收。 JDK8u40之后(包括这个版本),一旦没有其他对象引用了巨对象,那么巨对象也可以在年轻代集合中被回收。

4。 G1执行过程中出现异常情况

并发标记周期开始后的FULL GC

G1开始了标记周期,但是在并发标记完成之前,发生了一次Full GC,日志经常是这样的:

51.408:[GC并发标记开始]
65.473:[完整 GC 4095M->1395M(4096M),6.1963770 秒]
[时间:用户=7.87 系统=0.00,真实=6.20 秒]
71.669:[GC 并发标记中止]
GC并发标记启动开始后发生

FULL GC。这意味着老年代分区的回收速度比较慢,或者对象从新生代晋升到老年代的速度太快,或者大对象较多。直接分配在老年代。出于上述原因,我们可能需要做出的调整包括:增加整个堆的大小、更快地触发并发回收周期、允许更多的回收线程参与垃圾收集。

混合收集模式下的FULL GC

在GC日志中观察到混合收集后紧跟着FULL GC,这说明混合收集的速度太慢了。在老一代中释放足够的分区之前,应用程序会请求比当前剩余可用分区更多的分区。分配大的内存空间。针对这种情况我们可以进行调整:增加每个混合收集中收集的老年代分区数量;增加并发标记的线程数;并增加混合收集的频率。

疏散失败(转移失败)

新生代垃圾回收结束时,找不到可用的分区来接收幸存的对象。以下日志是常见的:

60.238:[GC暂停(年轻)(到空间溢出),0.41546900秒]

这意味着整个堆的碎片已经变得非常严重。我们可以从以下几个方面进行调整:(1)增加整个堆的大小——通过增加-XX:G1ReservePercent选项的值(并相应增加总堆大小),增加量为“目标空间”保留的内存; (2)通过减少-XX:InitiatingHeapOccupancyPercent提前启动标记周期; (3)
您还可以通过增加 -XX:ConcGCThreads 选项的值来增加并发标记线程的数量;

巨对象分配失败

如果你在GC日志中看到莫名其妙的FULL GC日志,并且与上面提到的情况不对应,那么你可以怀疑是巨对象分配导致的。这里我们可以考虑使用 jmap 命令执行堆转储,然后通过 MAT 分析堆转储文件。关于heap dump文件的分析技巧,后面会有专门的文章。

4。 G1调音

G1的调优目标主要是实现更短的暂停时间和更高的吞吐量,同时避免FULL GC和疏散失败。关于G1 GC的调优,需要记住以下几点:

  1. 不要自己显式设置新生代的大小(使用Xmn-XX:NewRatio参数)。如果显式设置新生代的大小,就会导致目标时间参数。无效的。

  2. 由于G1收集器已经有一套预测和调整机制,所以我们的第一选择就是信任它,即调整-XX:MaxGCPauseMillis=N参数,这也符合G1的目的——让GC调优尽可能简单。这里有一个权衡:如果减小这个参数的值,就意味着新生代的大小会减小,这也会导致新生代GC更频繁地发生。同时,也会导致回收周期混杂。老年代分区被回收的数量减少,从而增加了FULL GC的风险。这个时间设置得越短,应用程序的吞吐量也会受到影响。

  3. 混合垃圾收集的调整。如果调整预期最大停顿时间参数仍然不能解决问题,即日志中仍然可以看到FULL GC现象,那么就需要手动进行一些调整。可以进行的调整包括:

  • 调整G1垃圾收集的后台线程数量。通过设置参数-XX:ConcGCThreads=n,可以增加后台标记线程数量,帮助G1赢得这场互相追逐的游戏;
  • 调整G1垃圾收集器并发周期的频率。如果G1早点开始垃圾回收,也能帮助G1赢得这场比赛。这可以通过设置参数-XX:InitiatingHeapOccupancyPercent来实现这个目标,如果这个参数调小,G1会更早触发并发垃圾收集周期。这个值需要谨慎设置:如果这个参数设置得太高,会频繁发生FULL GC;如果该值设置太小,G1会频繁并发收集,浪费CPU资源。通过GC日志,可以判断某一时刻GC是否正常——一个并发周期结束后,需要保证堆中剩余空间小于InitiatingHeapOccupancyPercent的值。
  • 调整G1垃圾收集器混合收集的工作量,即一次混合垃圾收集处理尽可能多的分区,另一方面可以提高混合垃圾收集的频率。混合集合中可以回收多少个分区取决于三个因素:(1)有多少个分区被识别为垃圾分区,-XX:G1MixedGCLiveThresholdPercent=n该参数表示某个分区是否存活的对象比例超过n,则不会被选为垃圾分区。因此,您可以通过该参数控制每个混合集合的分区数量。该参数值越大,某个分区越容易被视为垃圾分区; (2) 在一个并发循环中,G1最多可以经历几个混合收集循环。这可以通过 -XX:G1MixedGCCountTarget=n 设置。默认是8。如果减少这个值,可以增加每次收集的混合收集数量。分区数量较多,但可能会导致停顿时间过长; (3) 最大预期GC暂停值由MaxGCPauseMillis参数确定。默认值为200ms,混合采集周期中的暂停时间向上。是的,如果实际运行时间小于这个参数,那么G1可以收集更多的分区。

5。 G1

最佳实践

1。关键参数

  • -XX:+UseG1GC,告诉JVM使用G1垃圾收集器
  • -XX:MaxGCPauseMillis=200,设置GC暂停时间的目标最大值。这是一个灵活的目标,JVM会尽力实现这个目标
  • -XX:INitiatingHeapOccupancyPercent=45,如果整个堆的使用率超过这个值,G1就会触发并发周期。请记住,这是整个堆空间的比例,而不是某一代的比例。

2.最佳实践

不要设置年轻代的大小

通过 -Xmn 显式设置年轻代大小会干扰 G1 收集器的默认行为:

  • G1 不再以设置的暂停时间为目标。也就是说,如果设置了年轻代的大小,则无法自适应调整以达到指定的暂停时间目标
  • G1 无法按需增长或缩小年轻代的规模

响应时间指标

请勿根据平均响应时间(ART)设置此参数-XX:MaxGCPauseMillis=n。您应该设置您希望 90% GC 能够达到的暂停时间。这意味着90%的用户请求都不会超过这个响应时间。记住,这个值是一个目标,但是G1并不能保证100%的GC停顿时间都能达到这个目标

3。 G1 GC 参数选项

参数名称 含义 默认
-XX:+使用G1GC 使用G1收集器 在JDK1.8中,还需要显式指定
-XX:MaxGCPauseMillis=n 设置预期的最大 GC 暂停时间。这是一个灵活的目标。 JVM会尽力实现这个目标 200
-XX:InitiatingHeapOccupancyPercent=n 当整个堆的空间使用百分比超过这个值时,就会触发并发收集周期。请记住,它是整个堆 45
-XX:NewRatio=n 新生代与老年代的比例 2
-XX:SurvivorRatio=n Eden空间与Survivor空间的比例 8
-XX:MaxTenuringThreshold=n 对象在新生代中经历的最多的新生代收集,或者说最大的岁数 G1中是15
-XX:ParallelGCThreads=n 设置垃圾收集器的并行阶段的垃圾收集线程数 不同的平台有不同的值
-XX:ConcGCThreads=n 设置垃圾收集器并发执行GC的线程数 n一般是ParallelGCThreads的四分之一
-XX:G1ReservePercent=n 设置作为空闲空间的预留内存百分比,以降低目标空间溢出(疏散失败)的风险。默认值是 10%。增加或减少这个值,请确保对总的 Java 堆调整相同的量 10
-XX:G1HeapRegionSize=n 分区的大小 堆内存大小的1/2000,单位是MB,值是2的幂,范围是1MB到32MB之间
-XX:G1HeapWastePercent=n 设置您愿意浪费的堆百分比。如果可回收百分比小于堆废物百分比,JavaHotSpotVM不会启动混合垃圾回收周期(注意,这个参数可以用于调整混合收集的频率)。 JDK1.8是5
-XX:G1MixedGCCountTarget=8 设置并发周期后需要执行多少次混合收集,如果混合收集中STW的时间过长,可以考虑增大这个参数。(注意:这个可以用来调整每次混合收集中回收掉老年代分区的多少,即调节混合收集的停顿时间) 8
-XX:G1MixedGCLiveThresholdPercent=n 一个分区是否会被放入mix GC的CSet的阈值。对于一个分区来说,它的存活对象率如果超过这个比例,则改分区不会被列入mixed gc的CSet中 JDK1.6和1.7是65,JDK1.8是85

常见问题

  1. Young GC、Mixed GC和Full GC的区别?
    答:Young GC的CSet中只包括年轻代的分区,Mixed GC的CSet中除了包括年轻代分区,还包括老年代分区;Full GC会暂停整个引用,同时对新生代和老年代进行收集和压缩。

  2. ParallelGCThreads和ConcGCThreads的区别?
    答:ParallelGCThreads指得是在STW阶段,并行执行垃圾收集动作的线程数,ParallelGCThreads的值一般等于逻辑CPU核数,如果CPU核数大于8,则设置为5/8 * cpus,在SPARC等大型机上这个系数是5/16。;ConcGCThreads指的是在并发标记阶段,并发执行标记的线程数,一般设置为ParallelGCThreads的四分之一。

  3. write barrier在GC中的作用?如何理解G1 GC中write barrier的作用?
    写屏障是一种内存管理机制,用在这样的场景——当代码尝试修改一个对象的引用时,在前面放上写屏障就意味着将这个对象放在了写屏障后面。write barrier在GC中的作用有点复杂,我们这里以trace GC算法为例讲下:trace GC有些算法是并发的,例如CMS和G1,即用户线程和垃圾收集线程可以同时运行,即mutator一边跑,collector一边收集。这里有一个限制是:黑色的对象不应该指向任何白色的对象。如果mutator视图让一个黑色的对象指向一个白色的对象,这个限制就会被打破,然后GC就会失败。针对这个问题有两种解决思路:(1)通过添加read barriers阻止mutator看到白色的对象;(2)通过write barrier阻止mutator修改一个黑色的对象,让它指向一个白色的对象。write barrier的解决方法就是讲黑色的对象放到写write barrier后面。如果真得发生了white-on-black这种写需求,一般也有多种修正方法:增量得将白色的对象变灰,将黑色的对象重新置灰等等。我理解,增量的变灰就是CMS和G1里并发标记的过程,将黑色的对象重新变灰就是利用卡表或SATB的缓冲区将黑色的对象重新置灰的过程,当然会在重新标记中将所有灰色的对象处理掉。关于G1中write barrier的作用,可以参考R大的这个帖子里提到的:

  4. G1里在并发标记的时候,如果有对象的引用修改,要将旧的值写到一个缓冲区中,这个动作前后会有一个write barrier,这段可否细说下?
    答:这块涉及到SATB标记算法的原理,SATB是指start at the beginning,即在并发收集周期的第一个阶段(初始标记)是STW的,会给所有的分区做个快照,后面的扫描都是按照这个快照进行;在并发标记周期的第二个阶段,并发标记,这是收集线程和应用线程同时进行的,这时候应用线程就可能修改了某些引用的值,导致上面那个快照不是完整的,因此G1就想了个办法,我把在这个期间对对象引用的修改都记录动作都记录下来,有点像mysql的操作日志。

  5. GC算法中的三色标记算法怎么理解?
    trace GC将对象分为三类:白色(垃圾收集器未探测到的对象)、灰色(活着的对象,但是依然没有被垃圾收集器扫描过)、黑色(活着的对象,并且已经被垃圾收集器扫描过)。垃圾收集器的工作过程,就是通过灰色对象的指针扫描它指向的白色对象,如果找到一个白色对象,就将它设置为灰色,如果某个灰色对象的可达对象已经全部找完,就将它设置为黑色对象。当在当前集合中找不到灰色的对象时,就说明该集合的回收动作完成,然后所有白色的对象的都会被回收。PS:这个问题来自参考资料17,我将原文也贴在下面:

    For a tracing collector (marking or copying), one conceptually colours the data white (not yet seen by the collector), black (alive and scanned by the collector) and grey (alive but not yet scanned by the collector). The collector proceeds by scanning grey objects for pointers to white objects. The white objects found are turned grey, and the grey objects scanned are turned black. When there are no more grey objects, the collection is complete and all the white objects can be recycled.

参考资料

  1. Understanding G1 GC Logs
  2. Garbage First Garbage Collector Tuning
  3. 垃圾优先型回收器调优
  4. Oracle的GC调优文档——G1
  5. The Garbage-First Garbage Collector
  6. 《Java性能权威指南》
  7. 《Java性能调优指南》
  8. G1入门,O记官网的PPT
  9. Java Hotspot G1 GC的一些关键技术
  10. G1 GC的论文
  11. R大关于G1 GC的帖子
  12. Tips for Tuning the Garbage First Garbage Collector
  13. Java性能调优指南
  14. Java性能权威指南
  15. G1: What are the differences between mixed gc and full gc?
  16. Part 1: Introduction to the G1 Garbage Collector
  17. Collecting and reading G1 garbage collector logs - part 2
  18. GC FAQ -- algorithms

本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

相关文章

最新资讯

热门推荐