g1垃圾收集器_G1:一个垃圾收集器来统治他们全部

g1垃圾收集器

许多文章描述了调优的垃圾收集器如何使应用程序的服务水平协议(SLA)承诺付诸东流。 例如,无法预期的长期垃圾收集暂停可能轻易超过其他性能良好的应用程序的响应时间要求。 此外,当您使用非紧凑型垃圾收集器(GC)(例如并发标记和清除(CMS))尝试通过串行(单线程)完整垃圾回收来回收其碎片堆时,异常情况会增加,该垃圾收集是stop-the-世界(STW)。

现在让我们在上面的段落中进行扩展:假设年轻一代中的分配失败触发了年轻集合,从而导致了向老一代的晋升。 此外,假设零散的旧一代没有足够的空间容纳新晋升的对象。 这样的条件将触发完整的垃圾回收周期,这将执行堆的压缩。

使用CMS GC,完整的集合是串行的和STW的,因此,在回收堆空间然后对其进行压缩的同时,您的应用程序线程将在整个时间内停止。 STW暂停的持续时间取决于您的堆大小和剩余的对象。

另外,即使您确实有并行(多线程)压缩来应对碎片,您仍然会得到完整的垃圾回收(涉及Java堆的所有代),而此时垃圾回收可能足以回收其中的一些垃圾。上一代的自由空间。

这是Parallel Old GC的常见情况。 使用Parallel Old,并行处理旧的STW完整垃圾收集暂停是对旧一代的回收。 完整的垃圾回收不是增量的; 这是一个很大的STW暂停,并且不与应用程序执行交错。

注意:您可以了解更多关于热点的GC ^ h ERE

有了以上信息,我们想以HotSpot最新的GC(在JDK7更新4中引入)“垃圾优先”(G1)收集器的形式考虑一种解决方案。

G1 GC是增量并行压缩GC,与CMS GC和Parallel Old GC相比,可提供更多可预测的暂停时间。 通过引入并行,并发和多阶段的标记周期,G1 GC可以处理更大的堆,同时提供合理的最坏情况下的暂停时间。 G1 GC的基本思想是设置堆范围(使用-Xms表示最小堆大小,使用-Xmx表示最大大小)和一个现实的(软实时)暂停时间目标(使用-XX:MaxGCPauseMillis) ,然后让GC尽其所能。

随着G1 GC的推出,HotSpot摆脱了传统的GC布局,在该布局中,连续的Java堆分为(连续的)年轻一代和老一代。 在G1 GC中,HotSpot引入了“区域”的概念。 单个大的连续Java堆空间分为多个固定大小的堆区域。 “自由”区域的列表将维护这些区域。 根据需要,将空闲区域分配给年轻一代或老一代。 这些区域的大小可能在1MB到32MB之间,具体取决于您的Java堆总大小。 目标是使总堆具有大约2048个区域。 释放区域后,它将返回到“空闲”区域列表。 G1 GC的原理是,首先收集活动数据量最少的区域(即,垃圾量最大的区域),以尽可能地回收Java堆(同时尽最大努力满足暂停时间目标)。 因此,名称为垃圾优先

图1:常规GC布局

需要注意的一件事是,对于G1 GC,年轻一代和老一代都不必是连续的。 这是一个方便的功能,因为世代的大小现在更加动态。

像Parallel Old GC这样的自适应大小的GC算法最终会保留每一代可能需要的额外空间,以便它们可以适应其连续的空间约束。 如果是CMS,则需要完整的垃圾回收来调整Java堆和世代的大小。

相比之下,G1 GC使用逻辑世代(年轻世代的不连续区域的集合,而旧世代的其余部分的集合),因此在空间或时间上没有太多浪费。

可以肯定的是,G1 GC算法确实利用了HotSpot的一些基本概念。 例如,分配,复制到幸存者空间以及升级到老一代的概念与以前的HotSpot GC实施类似。 伊甸园地区和幸存者地区仍然构成了年轻一代。 除“巨大”分配外,大多数分配都在伊甸园中进行。 (注意:对于G1 GC,跨越区域大小一半以上的对象被视为“大型对象”,并直接分配到旧一代中的“大型”区域中。)G1 GC根据您的暂停选择自适应的年轻一代大小时间目标。 年轻代的范围从预设的最小大小到预设的最大大小都可以,这是Java堆大小的函数。 当伊甸园达到容量极限时,将发生“年轻垃圾收集”,也称为“疏散暂停”。 这是STW暂停,可将活动物体从构成伊甸园的区域复制(撤离)到“到太空”幸存者区域。

图2:垃圾优先GC布局

此外,来自“从太空”幸存者区域的活动对象将被复制到“到太空”幸存者区域,或者基于对象的年龄和“持续时间阈值”,将被提升到以下区域:老一代的空间。

每个年轻的收藏都涉及并行工作时间和顺序/串行时间。 为了进一步解释这一点,我将使用最新的Java 7更新发行版的日志输出,该发行版在发布时为7u25。 (我们还有7u40的抢先体验 (EA)。请随时为您的平台试用EA捆绑包。使用7u40 EA,您可能会发现日志格式有所不同,但基本前提保持不变。)

此后,以下命令行选项生成了GC日志输出–

java –Xmx1G –Xms1G –XX:+ UseG1GC –XX:+ PrintGCDetails –XX:+ PrintGCTimeStamps GCTestBench

注意:我使用默认的200ms暂停时间目标。

0.189: [GC pause (young), 0.00080776 secs]
   [Parallel Time: 0.4 ms]
      [GC Worker Start (ms): 188.7 188.7 188.8 188.8
       Avg: 188.8, Min: 188.7, Max: 188.8, Diff: 0.1]
      [Ext Root Scanning (ms): 0.2 0.2 0.2 0.1
       Avg: 0.2, Min: 0.1, Max: 0.2, Diff: 0.1 ]
      [ Update RS (ms): 0.0 0.0 0.0 0.0
       Avg: 0.0, Min: 0.0, Max: 0.0, Diff: 0.0]
         [Processed Buffers : 0 0 0 1
          Sum: 1, Avg: 0, Min: 0, Max: 1, Diff: 1 ]
      [ Scan RS (ms): 0.0 0.0 0.0 0.0
       Avg: 0.0, Min: 0.0, Max: 0.0, Diff: 0.0 ]
      [ Object Copy (ms): 0.2 0.2 0.1 0.2
       Avg: 0.2, Min: 0.1, Max: 0.2, Diff: 0.0 ]
      [ Termination (ms): 0.0 0.0 0.0 0.0
       Avg: 0.0, Min: 0.0, Max: 0.0, Diff: 0.0]
         [Termination Attempts : 1 2 1 2
          Sum: 6, Avg: 1, Min: 1, Max: 2, Diff: 1 ]
      [GC Worker End (ms): 189.1 189.1 189.1 189.1
       Avg: 189.1, Min: 189.1, Max: 189.1, Diff: 0.0]
      [GC Worker (ms): 0.4 0.4 0.3 0.3
       Avg: 0.4, Min: 0.3, Max: 0.4, Diff: 0.1]
      [ GC Worker Other (ms): 0.0 0.0 0.1 0.1
       Avg: 0.1, Min: 0.0, Max: 0.1, Diff: 0.1]
   [Clear CT: 0.2 ms]
   [Other: 0.2 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.2 ms]
      [Ref Enq: 0.0 ms]
      [Free CSet: 0.0 ms] 

缩进划分了并行工作组和顺序工作组。 并行工作时间进一步细分为-

  1. 外部根扫描:并行GC工作线程在扫描指向集合集的外部根(例如寄存器,线程堆栈等)上花费的时间。
  2. 更新记忆集(RSets): RSets协助G1 GC跟踪指向区域的参考。 此处显示的时间是并行工作线程在更新RSets上花费的时间。
  3. 已处理的缓冲区:该计数显示工作线程处理了多少个“更新缓冲区”。
  4. 扫描RSets:RSets扫描到区域中所花费的时间。 这段时间将取决于RSet数据结构的“粗度”。
  5. 对象复制:在每个年轻的收集期间,GC都会将所有来自伊甸园和“从太空”幸存者的实时数据复制到“到太空”幸存者的区域中或到旧世代区域。 此处列出了工作线程完成此任务所花费的时间。
  6. 终止:完成其特定的工作(例如,对象扫描和复制)后,每个工作线程都会输入其“终止协议”。 在终止之前,辅助线程从其他线程中寻找工作以进行窃取并在没有线程时终止。 此处列出的时间表示终止工作线程所花费的时间。
  7. 并行工作者“其他”时间:工作者线程花费的时间,没有在上面列出的任何并行活动中计算。

顺序工作 (可以单独并行化)分为-

  1. 清除CT :GC工作线程在清除RSet扫描元数据的卡表中花费的时间。
  2. 其他一些人则在“ 其他 ”时间里加入了俱乐部,包括:
    • 选择收集集(CSet) :垃圾收集周期收集CSet中的区域集。 收集暂停收集/撤出特定CSet中的所有实时数据。 此处列出的时间是最终确定添加到CSet的区域集所花费的时间。

    • 引用处理 :处理先前垃圾收集阶段中延迟的引用(软,弱,最终和幻像)所花费的时间。

    • 引用排队 :将引用放置到待处理列表上所花费的时间。

    • Free CSet :释放刚刚收集的区域集所花费的时间。 这还包括释放其RSet所花费的时间。

我刚刚对诸如RSets,其粗化,更新缓冲区,CSet之类的许多内容进行了略读,在接下来的几段中,将有更多内容,例如“快照快照”(SATB)但是,为了进一步了解它们,我们将不得不“深入研究” G1 GC的内部结构,这是一个有趣的话题,不在本文讨论范围之内。

现在我们了解了年轻的收藏如何开始填充老一代,我们需要引入(并理解)“标记阈值”的概念。 当总堆的占用量超过此阈值时,G1 GC将触发一个多阶段的并发标记周期。 设置阈值的命令行选项为–XX:InitiatingHeapOccupancyPercent ,默认为Java堆总大小的45%。 G1 GC使用一种称为Snapshot-At-The-Beginning(SATB)的标记算法,该算法在标记周期的“开始”时获取堆中活动对象集的逻辑快照。 该算法使用预写屏障来记录和标记属于逻辑快照的对象。 现在,让我们花一些时间讨论多阶段并发标记的各个阶段,首先看一下GC日志的输出:

0.078: [GC pause (young) (initial-mark ), 0.00262460 secs]
   [Parallel Time: 2.3 ms]
      [GC Worker Start (ms): 78.1 78.2 78.2 78.2
       Avg: 78.2, Min: 78.1, Max: 78.2, Diff: 0.1]
      [Ext Root Scanning (ms): 0.2 0.1 0.2 0.1
       Avg: 0.2, Min: 0.1, Max: 0.2, Diff: 0.1]
      [Update RS (ms): 0.2 0.2 0.2 0.2
       Avg: 0.2, Min: 0.2, Max: 0.2, Diff: 0.0]
         [Processed Buffers : 2 3 2 2
          Sum: 9, Avg: 2, Min: 2, Max: 3, Diff: 1]
      [Scan RS (ms): 0.0 0.0 0.0 0.0
       Avg: 0.0, Min: 0.0, Max: 0.0, Diff: 0.0]
      [Object Copy (ms): 1.8 1.8 1.8 1.8
       Avg: 1.8, Min: 1.8, Max: 1.8, Diff: 0.0]
      [Termination (ms): 0.0 0.0 0.0 0.0
       Avg: 0.0, Min: 0.0, Max: 0.0, Diff: 0.0]
         [Termination Attempts : 1 1 1 1
          Sum: 4, Avg: 1, Min: 1, Max: 1, Diff: 0]
      [GC Worker End (ms): 80.4 80.4 80.4 80.4
       Avg: 80.4, Min: 80.4, Max: 80.4, Diff: 0.0]
      [GC Worker (ms): 2.2 2.2 2.2 2.2
       Avg: 2.2, Min: 2.2, Max: 2.2, Diff: 0.1]
      [GC Worker Other (ms): 0.0 0.1 0.1 0.1
       Avg: 0.1, Min: 0.0, Max: 0.1, Diff: 0.1]
   [Clear CT: 0.2 ms]
   [Other: 0.2 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.1 ms]
      [Ref Enq: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 3072K(5120K)->0B(5120K) Survivors: 1024K->1024K Heap: 16M(32M)->16M(32M)]
  [Times: user=0.06 sys=0.00, real=0.00 secs] 
   0.081: [ GC concurrent-root-region-scan-start ]
   0.082: [ GC concurrent-root-region-scan-end, 0.0009122 ]
   0.082: [ GC concurrent-mark-start ]
<snip> [Zero or more embedded young garbage collections are possible here,
          but removed for brevity.]
   0.094: [ GC concurrent-mark-end, 0.0115579 sec ]
   0.094: [ GC remark 0.094: [GC ref-proc, 0.0000033 secs], 0.0004374 secs]
          [Times: user=0.00 sys=0.00, real=0.00 secs ] 
   0.094: [ GC cleanup 22M->10M(32M), 0.0003031 secs ]
          [ Times: user=0.00 sys=0.00, real=0.00 secs ] 
   0.095: [ GC concurrent-cleanup-start ]
   0.095: [ GC concurrent-cleanup-end, 0.0000350 ]

此外,以下是详细信息:

  • 初始标记阶段 – G1 GC在初始标记阶段标记根。 这就是上面输出的第一行告诉我们的。 初始标记阶段是在常规(STW)年轻垃圾收集上进行的(同时完成)。 因此,输出类似于您在疏散暂停时看到的输出。
  • 根区域扫描阶段 –在此阶段中,G1 GC扫描初始标记阶段的幸存者区域,以查找旧一代的引用并标记引用的对象。 此阶段与应用程序同时运行(不是STW)。 在下一个年轻垃圾收集发生之前,完成此阶段很重要。
  • 并发标记阶段 –在此阶段,G1 GC在整个Java堆中寻找可访问的(活动的)对象。 此阶段与应用程序同时发生,并且年轻的垃圾回收会中断并发标记阶段( 如上所示 )。
  • 备注阶段 –备注阶段有助于完成标记。 在此STW阶段,G1 GC耗尽所有剩余的SATB缓冲区并跟踪所有尚未访问的活动对象。 G1 GC在备注阶段也进行参考处理。
  • 清理阶段 –这是多阶段标记周期的最后阶段。 这是当G1 GC做现场岬会计部分STW(识别完全自由区和混合垃圾回收的候选区域),当G1 GC擦洗RSets。 当G1 GC重置并将空区域返回到空闲列表时,它部分并发

一旦G1 GC成功完成并发标记周期,它就会具有启动旧版本收集所需的信息。 到目前为止,由于G1 GC没有与这些区域相关的任何标记信息,因此无法收集旧区域。 有助于压实和疏散旧世代的集合被适当地称为“混合”集合,因为G1 GC不仅会收集伊甸园和幸存者区域,而且(可选)还会向混合物中添加旧区域。 现在让我们讨论一些对理解混合集合很重要的细节。

混合收集可以(并且通常)在多个混合垃圾收集周期中发生。 当收集到足够数量的旧区域时,G1 GC将恢复执行新的垃圾收集,直到下一个标记周期完成。 此处列出和定义的许多标志控制添加到CSets的旧区域的确切数目:

–XX:G1MixedGCLiveThresholdPercent :要包含在混合集合中的旧区域中的活动对象的占用阈值。

–XX:G1HeapWastePercent :您可以在堆中容忍的垃圾阈值。

–XX:G1MixedGCCountTarget :混合垃圾回收的目标数量,应在其中收集最多具有G1MixedGCLiveThresholdPercent个实时数据的区域。

–XX:G1OldCSetRegionThresholdPercent :混合收集期间可以收集的最大旧区域数的限制。

让我们看一下G1 GC日志的混合收集周期输出:

1.269: [GC pause (mixed ), 0.00373874 secs]
   [Parallel Time: 3.0 ms]
      [GC Worker Start (ms): 1268.9 1268.9 1268.9 1268.9
    Avg: 1268.9, Min: 1268.9, Max: 1268.9, Diff: 0.0]
   [Ext Root Scanning (ms): 0.2 0.2 0.2 0.1
    Avg: 0.2, Min: 0.1, Max: 0.2, Diff: 0.1]
   [Update RS (ms): 0.0 0.0 0.0 0.0
    Avg: 0.0, Min: 0.0, Max: 0.0, Diff: 0.0]
      [Processed Buffers : 0 0 0 1
       Sum: 1, Avg: 0, Min: 0, Max: 1, Diff: 1]
   [Scan RS (ms): 0.1 0.0 0.0 0.1
    Avg: 0.1, Min: 0.0, Max: 0.1, Diff: 0.1]
   [Object Copy (ms): 2.6 2.7 2.7 2.6
    Avg: 2.7, Min: 2.6, Max: 2.7, Diff: 0.1]
   [Termination (ms): 0.1 0.1 0.0 0.1
    Avg: 0.0, Min: 0.0, Max: 0.1, Diff: 0.1]
      [Termination Attempts : 2 1 2 2
       Sum: 7, Avg: 1, Min: 1, Max: 2, Diff: 1]
   [GC Worker End (ms): 1271.9 1271.9 1271.9 1271.9
    Avg: 1271.9, Min: 1271.9, Max: 1271.9, Diff: 0.0]
   [GC Worker (ms): 3.0 3.0 3.0 2.9
    Avg: 3.0, Min: 2.9, Max: 3.0, Diff: 0.0]
   [GC Worker Other (ms): 0.1 0.1 0.1 0.1
    Avg: 0.1, Min: 0.1, Max: 0.1, Diff: 0.0]
  [Clear CT: 0.1 ms]
  [Other: 0.6 ms]
   [Choose CSet: 0.0 ms]
   [Ref Proc: 0.1 ms]
   [Ref Enq: 0.0 ms]
   [Free CSet: 0.3 ms]

总而言之,G1通过引入构成逻辑代的区域的概念来改进其先前的GC。 这些区域有助于为旧一代的增量收集提供更好的粒度。 G1通过复制实时数据完成其大部分回收,从而实现了压缩。 这绝对是从空间上的分配而不进行压缩的基础上的进步,这使老一辈看起来像瑞士奶酪! Ĵ

回收的第一级发生在(多阶段标记周期的)清理阶段,当回收“完全”空闲(即充满垃圾)的区域并返回到空闲列表时。 下一个级别发生在增量混合垃圾收集期间。 如果所有其他方法均失败,则将收集整个J​​ava堆。 这是众所周知的故障保护完整垃圾回收。

所有以上这些使老一代的开垦变得更加容易,并且采用了分层的方式。

我希望本文能帮助您大致了解G1 GC的差异和组成。 感谢您的收看!

编者注:请继续关注第2部分,即将在2013年9月发布,在那里Monica将讨论一些高级主题,并提供有关如何使用这些指标来调整应用程序性能的一些建议。

翻译自: https://www.infoq.com/articles/G1-One-Garbage-Collector-To-Rule-Them-All/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

g1垃圾收集器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值