广东圆心网站开发,厦门海绵城市建设官方网站,北京网站建设资讯,小吃加盟方案导航引言一、G1 介绍1.1 适用场景1.2 设计初衷1.3 关注焦点1.4 工作模式1.5 堆的逻辑结构1.6 主要收集目标1.7 停顿预测模型1.8 拷贝和压缩1.9 与 CMS 和 Parallel 收集器的比较1.10 固定停顿目标二、堆的逻辑分区2.1 region2.2 CSet2.3 RSet2.4 Card Table三、G1 的工作原理3.…
导航引言一、G1 介绍1.1 适用场景1.2 设计初衷1.3 关注焦点1.4 工作模式1.5 堆的逻辑结构1.6 主要收集目标1.7 停顿预测模型1.8 拷贝和压缩1.9 与 CMS 和 Parallel 收集器的比较1.10 固定停顿目标二、堆的逻辑分区2.1 region2.2 CSet2.3 RSet2.4 Card Table三、G1 的工作原理3.1 YGC3.2 Mixed GC3.3 工作流程四、常用的G1调优参数和建议4.1 G1常见命令行参数及其默认值4.2 调优建议4.3 to-space 的溢出和耗尽4.4 Humongous 对象及其分配引言
本文针对 Hotspot 虚拟机的 G1 垃圾收集器进行总结和归纳适用于JDK 8。
文章内容以 Garbage-First Garbage Collector 和 Garbage-First Garbage Collector Tuning 两篇官方文章为主结合 JVM面试必问G1垃圾回收器 技术社区分享汇总。
一、G1 介绍
G1 是 Hotspot 虚拟机在 jdk 8 之后正式java7处于试验阶段推出的一款针对多核CPU、大内存的服务器端应用程序提供服务的垃圾收集器。
1.1 适用场景
通用需要大堆空间6G、低延迟 0.5s 业务需要堆中有 超过50% 的存活对象对象的分配率和提升率差异很大。
1.2 设计初衷
实现高吞吐量的同时获得尽可能短暂的STW停顿。
1.3 关注焦点
为用户提供一种更大的堆且低延迟的解决方案。例如6G的堆且延迟基本可以稳定在小于0.5秒。
1.4 工作模式
面向整个堆空间进行GC某些阶段如并发标记可与工作线程并发执行。
1.5 堆的逻辑结构
整个堆被分成一个个大小相等且物理地址连续的 region分区。region大小在1M~32M不等2的幂取决于堆的大小但总数不会超过2048。
为兼容传统的分代机制eden、survivor、old 这些年代概念依然存在但在G1中是以 region 为单位分散在堆中相同或不同的年代之间不一定连续。
1.6 主要收集目标
Garbage First 垃圾优先。
通过全局的并发标记G1可以得知哪些区域大部分都是垃圾G1会首先收集和压缩这些region。优先收集垃圾更多的 region就意味着可以以更短的时间获得更大的收益。
1.7 停顿预测模型
G1会使用一种称为“暂停预测”的模型来评估需要收集多少region从而限制停顿时间STW。
1.8 拷贝和压缩
对于内存碎片问题G1在GC的过程中会将一个或多个 region中的存活对象拷贝、压缩到一个新的region 中以减少堆空间的碎片化。
这项操作必须是 STW的因为要拷贝到新的region如果应用程序不停的分配对象很明显会严重干扰拷贝压缩的过程。
这个过程会在多处理器上并行执行以降低停顿时长。因此每次GCG1都在努力减少内存碎片。
1.9 与 CMS 和 Parallel 收集器的比较
G1要优于这两种收集器。
CMS 默认是不进行内存压缩的所以会产生严重的内存碎片问题 Parallel收集器会对整个堆进行数据压缩这会导致严重的STW。
1.10 固定停顿目标
用户可以设定STW的停顿时间但G1只是 大概率能够满足而并非绝对 。它会根据之前GC的情况会构建一个相当精确的“收集成本模型”以此来决定要收集哪些region以及收集多少region。 a reasonably accurate model of the cost of collecting the regions 二、堆的逻辑分区
2.1 region 上图是Oracle官网给出的G1收集器对整个堆结构的逻辑划分示例。
G1 将整个堆划分为一个个相同大小的 region。它覆盖了一块连续的内存空间大小在 1M ~ 32M不定但一定是 2 的幂默认数量是2048个。
和传统的垃圾收集器如Serial、Parallel、CMS等等不同G1只是在逻辑上分代。 也就是说G1摒弃了传统的大块分代空间的做法以 region 为单元将 region标记为年轻代或老年代这些region共同组成年轻代或老年代的整体大小。而且G1可以随着运行情况和策略的变化动态调整年轻代或老年代region的数量以此来调整对应分代的大小。
2.2 CSet
CSet Collection Set 它是一组可被回收的 region 集合。在后面讲到的 YGC 中会提到它的使用。CSet 中的 region 既可以是年轻代也可以是老年代G1 会将 CSet中存活的对象拷贝、压缩到新的regionCSet 大概占用堆空间的 1% 大小。
2.3 RSet
对于多数分代收集器YGC 会先收集堆中年轻代的内存空间这是一种增量收集的方式。G1作为逻辑分代的收集器同样属于增量式垃圾收集器。
在进行增量收集的时候收集器需要知道未收集的对象有没有指向正在收集的对象的引用。对于 YGC 来说通常是老年代指向年轻代。而 RSet 就是这样一种记忆结构专门用于存储一组其他对象指向当年对象的引用。
对于 G1 而言RSet 是位于 region 中。之所以设计这样一个结构就是为了在做 GC 标记的时候避免全图扫描。这也是 G1 高效 GC 的关键手段之一。
2.4 Card Table
Card Table 是一中特殊类型的 RSet。 A card table is a particular type of remembered set. —— Card Tables and Concurrent Phases Hotspot 虚拟机使用一组字节数组来实现 Card Table。每个字节代表一个 cardcard又对应堆中的一块连续内存地址。如果引用关系发生改变就将对应的 card 标记为 dirty在 remark 阶段就只需要扫描这些 dirty card 就可以快速追踪引用发生变化的对象。
三、G1 的工作原理
G1的工作过程是一个非常复杂的流程包含很多可考虑的因素和条件。
3.1 YGC
YGC 又叫做 young collection 或 Minor GC会有STW停顿。它是G1收集器的先锋兵实际上不光是G1其他收集器在最初发生GC的时候都要先进行 YGC。
Java中的大多数对象都主要分配在堆的 Eden 区它隶属于年轻代同时又随着应用程序的运行快速“消亡”对象不可达因此 YGC 是回收这些对象内存空间的有效手段。
在 YGC 期间G1会从 CSet 中获取需要收集的 eden 和 survivor 区。CSet 是 Collection Set 的缩写它是用于记录需要做垃圾收集的 region集合。G1 通过将活动对象从 eden 和 survivor 区增量并行复制到一个或多个不同的新 region 来实现内存压缩减少碎片。
不同对象的目标region取决于对象的分代年龄当达到年龄阈值就会分配到老年代 region 中而年龄不足的会被分配到 survivor region中并被包含在下一次的YGC 或 Mixed GC的CSet中。
值得一提的是在STW的YGC期间还会额外做一些有备后用的操作如初始标记这个过程是后续执行并发标记的“必要前戏”目标是标记出 GC roots 根节点由于本身就需要STW因此放在YGC的STW中完成是希望能够充分利用停顿时间以此达到G1的设计目标——pause time goal。
3.2 Mixed GC
Mixed GC 是在 YGC 的基础之上选择性的增加 old region 的清理。
在 YGC 的过程中G1会进行初始标记工作之后G1的垃圾回收线程就会进入重要的并发标记环节。
concurrent mark 阶段是与用户线程并发执行的垃圾收集工作可以被 YGC 的 STW 打断。其目标是标记出所有可达的对象三色标记算法RSet。 Concurrent marking phase: The G1 GC finds reachable (live) objects across the entire heap. This phase happens concurrently with the application, and can be interrupted by STW young garbage collections. G1的设计目标是尽可能在用户设定的停顿时间之内完成垃圾收集为了达到这一点并发执行一部分工作是必要的但带来的问题就是并发工作的GC线程势必会占用原本应该去执行用户线程的CPU资源造成的损失就是吞吐量的下降官方给出的参考数据是 90%的用户线程时间和 10%的GC时间而Parallel收集器的数据是 99% 的用户线程时间和 1%的GC时间。
在并发标记阶段完成后G1 的 YGC就会切换到 Mixed GC。
Mixed GC 阶段G1会选择性的将一些 old region 加入到待收集的CSet 中随 eden、survivor一同被G1清理。当G1清理了足够的 old region后多次Mixed GCG1就再次切换回 YGC直到下次并发标记完成。
总之G1的GC过程可以用这样一段描述来概括 一开始G1使用YGC进行年轻代的垃圾清理拷贝、压缩存活的对象但当堆内存可用空间渐渐不足例如活动对象占用超过整个堆的45%这一阈值时就会触发G1的标记周期这一过程以并发标记为主当标记结束后G1启动 MixedGC它不仅会清理年轻代还会选择性的清理一些老年代当经过了几轮的 MixedGC之后G1已经收集了足够多的老年代区域此时堆的空间也不再紧张G1就会再次切回YGC进行垃圾回收。 3.3 工作流程
YGC 和 Mixed GC 实际上是 G1 的两种工作模式对于 Mixed GC完整的工作流程应该包含以下几个阶段。
初始标记Initial Mark 这个阶段仅仅只是标记GC Roots能直接关联到的对象并修改TAMS(Next Top at Mark Start)的值让下一阶段用户程序并发运行时能在正确的可用的Region中创建新对象这阶段需要停顿线程但是耗时很短。而且是借用进行Minor GC的时候同步完成的所以G1收集器在这个阶段实际并没有额外的停顿
并发标记Concurrent Mark从GC Roots开始对堆中对象进行可达性分析递归扫描整个堆里的对象图找出存活的对象这阶段耗时较长但是可以与用户程序并发执行。当对象图扫描完成以后还要重新处理SATB记录下的在并发时有引用变动的对象。
最终标记Final Mark 对用户线程做另一个短暂的暂停用于处理并发阶段结束后仍遗留下来的最后那少量的 SATB 记录。
筛选回收(Live Data Counting and Evacuation) 负责更新 Region 的统计数据对各个 Region 的回收价值和成本进行排序根据用户所期望的停顿时间来制定回收计划。可以自由选择多个Region来构成会收集然后把回收的那一部分Region中的存活对象疏散到新的 region中。
四、常用的G1调优参数和建议
G1 是一个自适应的垃圾收集器一些默认参数足可以高效工作。以下是 G1 相关的控制参数了解这些参数的含义可以帮助我们更好的理解 G1 收集器。
4.1 G1常见命令行参数及其默认值
参考Important Defaults
参数和默认值说明-XX:G1HeapRegionSizen设置 G1 的region的大小1M ~ 32M 2的幂。目标是以-Xms为准大约有 2048 个 region-XX:MaxGCPauseMillis200设置期望的最大停顿时间。默认为 200ms-XX:G1NewSizePercent5年轻代占总堆的最小百分比。默认 5%。该参数为实验参数必须在该参数前面设置-XX:UnlockExperimentalVMOptions 解锁实验参数才能生效-XX:G1MaxNewSizePercent60年轻代占总堆最大百分比应和上一条参数互补。-XX:ParallelGCThreadsn设置 STW 时垃圾收集线程数将 n 设置为逻辑处理器的数量但最大不要超过 8。如果逻辑处理器数量超过8可以将 n 设置为逻辑处理器的 5/8 。-XX:ConcGCThreadsn设置并行标记的线程数。可以将 n 设置为 ParallelGCThreads 参数的 1/4 。-XX:InitiatingHeapOccupancyPercent45设置足以触发初始标记的堆占用阈值。默认为总堆大小的 45%。-XX:G1MixedGCLiveThresholdPercent85设置 Mixed GC 开始回收老年代 region 的堆占用阈值默认为 85%。这是一个实验性参数须在该参数前面设置-XX:UnlockExperimentalVMOptions 解锁实验参数才能生效。-XX:G1HeapWastePercent5设置允许浪费的堆空间占比-XX:G1OldCSetRegionThresholdPercent10设置可以被 Mixed GC 回收的老年代 region 占比上限。默认为 10%-XX:G1ReservePercent10设置预留的空内存大小以防 to survivor 区溢出。默认为 10%。如果你要调整该参数请记得将总堆的大小也一同调整。
4.2 调优建议
年轻代的大小 避免显式设置年轻代的大小因为 G1 会根据停顿目标自动调整年轻代的分配大小。STW限定目标 任何垃圾收集器都存在吞吐量和延迟的权衡。 G1 GC 的吞吐量目标是 90% 的应用程序时间和 10% 的垃圾收集时间。而 Parallel 收集器是 99% 的应用程序时间和 1% 的垃圾收集时间。因此如果想要优化吞吐量就必须要降低延迟的标准。调试 Mixed GC 当你在调试 Mixed GC时可以测试以下这些参数 -XX:InitiatingHeapOccupancyPercent改变触发标记周期的阈值 -XX:G1MixedGCLiveThresholdPercent 和 -XX:G1HeapWastePercent改变 Mixed GC 的决策 -XX:G1MixedGCCountTarget 和 -XX:G1OldCSetRegionThresholdPercent 调整 CSet 中老年代的数量
4.3 to-space 的溢出和耗尽
如果GC日志中出现to-space overflow或to-space exhausted这样的消息就表示G1 GC没有足够的内存来存放 survivor 或提升的对象。
这种情况可能是由于堆已经达到了最大值。例如
924.897: [GC pause (G1 Evacuation Pause) (mixed) (to-space exhausted), 0.1957310 secs]
924.897: [GC pause (G1 Evacuation Pause) (mixed) (to-space overflow), 0.1957310 secs]可以尝试调整以下参数
-XX:G1ReservePercent调大该参数以及相应的总堆大小以增加 “to-space” 的预留空间。-XX:InitiatingHeapOccupancyPercent调小该参数以更早的触发标记过程。-XX:ConcGCThreads调大该参数以增加并行标记线程的数量。
4.4 Humongous 对象及其分配
humongous 意为“极大的硕大的”。对于 G1 来说只要对象大小超过了 region 的一半都称为 humongous 对象。
humongous 对象会被直接分配到老年代的 humongous region有时 humongous 对象可能会跨越多个 region 分区。
在G1分配 humongous 之前都会检查标记阈值如果有必要就会触发初始标记为 Mixed GC 做准备。
为了减少拷贝的开销一般不会轻易拷贝压缩 humongous 对象G1 会在恰当的时机压缩 humongous 对象。
如果发现因为 humongous 对象的分配而频繁触发Mixed GC并且老年代还在不断飙升那么可以适当增加 region 的大小这样以前的 humongous 对象就有可能不会超过 region 的一半从而遵循常规分配策略。