首页 > 编程知识 正文

g1垃圾收集器优缺点,g1垃圾回收器管理最大内存

时间:2023-05-06 13:48:18 阅读:189657 作者:2091

本文详细介绍了G1垃圾收集器的参数配置、性能调优方法以及GC性能的分析与评估方法。

文章目录0. G1简介1 .垃圾回收阶段简介,纯年轻一代模式垃圾收集w3 .混合模式垃圾收集!标记周期各阶段5 .常用参数和默认值Come Come Everybody xx 3360 G1 heapregionsize=Come Come Everybody xx : maxgcpausemillis=200xx 3360 G1 new size percent=5 xx : G1 maxnewsizepercent=60xx 3360 parallelgcthreads=xx : concgcthreadsxx 3360 initiatingheapoccupancypercent=45 xx : G1 mixedgclivethresholdpercent=65xx 3360 G1 heapwastepercent-10xx : G1 oldcsetregionthresholdpercent-10 xx : G1 reserve percent-106.实验解锁JVM参数方法7 .最佳实践和推荐8. GC日志中内存溢出和内存不足的信息9 .大对象/大对象

0. G1概述

所有的G1都称为Garbage First Garbage Collector,它是内置在HotSpot JVM中的服务器端垃圾收集器。

G1【分代算法】,通过将GC进程分解为多个同时并行的阶段,分散休止时间,实现了低延迟特性,维持了良好的吞吐量。

只要G1认为可以收集垃圾,就触发GC一次。 当然,G1会优先回收生存数据少的区域。

生存数据少,表示其中垃圾对象多,这也是名字Garbage First的由来。

垃圾收集器本质上是内存管理工具。 G1算法主要通过以下方式实现自动内存管理。

【按年龄划分】给年轻一代分配新的对象,达到一定年龄的对象提高到老的年代。 【合并】在合并标记阶段,遍历旧时代的所有生存对象。 当Java堆内存的总使用量超过阈值时,HotSpot将触发标记周期。 【整理】采用并行复制方式整理生存对象,释放可用内存。 在GC中,并行指多个GC线程在一起工作,同时(concurrent )指GC线程和业务线程在一起同时执行。

本文首先简要介绍G1参数的构成方法,然后介绍GC性能的分析和评价方法。

要进行GC调整,至少需要了解Java垃圾回收的工作原理。

G1是增量式的分代垃圾收集器。 什么是增量?

将G1堆存储器分为同样大小的【小区域,小块】(region )。

启动JVM时,根据堆内存的配置确定每个region的大小。 region的大小取1MB到32MB的范围,总数通常不超过2048。

在G1中,新生代(eden )、生存区(survivor )、老年代)都是逻辑概念,是这些region的组合,这些region之间不需要连续。

可以设置参数以指定“预期的最大暂停时间”,G1将尽可能满足此软实时目标。

在【纯杨模式】垃圾收集期间,g-1可以动态地调整年轻一代的大小(eden survivor ),以实现该软实时目标的暂停时间。

在【混合模式(mixed )】的垃圾回收过程中,G1可以调整本次GC应回收的老一代region数,可以调整【应回收的总region数】、【每region生存对象的比例】以及【狒狒】

采用G1【增量并行复制】方式实现【堆内存碎片整理功能】,将回收中的生存对象复制到新的region中。 回收的英语为Collection Set,简称CSet,是本次GC涉及的region集合。

目标是尽可能多的从空闲的region中回收堆内存,同时试图达到预期的暂停时间指标。

G1对每个region分别设定了【记忆集】。 英语是Remembered Set,简称RSet,用于跟踪另一个region对此region的引用。

通过这种region分割和独立的RSet数据结构,G1可以并行执行增量垃圾收集,而无需遍历整个堆存储器。

因为只要扫描RSet,就可以知道对哪个跨度的引用指向这个region,并回收这些region。

G1【后光屏障】(postwrite barrier )记录堆内存修改信息,负责RSet更新。

1 .垃圾回收的阶段概述G1垃圾回收的纯年轻一代模式GC和混合模式GC除了转变暂停的STW阶段之外,还包括并行的多个子阶段

使用G1-start shot算法(SATB,snapshotat99 Come Come Everybody thebeginning ),在标记周期开始时生成堆内存中生存对象信息的一个快照。

在这种情况下,存活对象的总和包括启动快照时的存活对象和启动标记后新创建的对象。

G1的标记算法使用【预写屏障】

】(pre-write barrier)来记录和标记逻辑上属于这次快照的对象。

2. 纯年轻代模式的垃圾收集

G1将绝大部分的内存分配请求打到eden区。
在年轻代模式的垃圾收集过程中,G1会收集eden区和前一次GC使用的存活区。
并将存活对象拷贝/转移到一些新的region里面, 具体拷贝到哪里则取决于对象的年龄;
如果达到一定的GC年龄,就会转移/提升到老年代中;否则就会转移到存活区。
本次的存活区则会被加入到下一次年轻代GC/混合模式GC的CSet中。

3. 混合模式的垃圾收集

并发标记周期执行完毕之后,G1则会从纯年轻模式切换到混合模式。
在执行混合模式的垃圾收集时,G1会选择一部分老年代region加入回收集,当然,每次的回收集都包括所有eden区和存活区。
具体一次添加多少个老年代region,由哪些参数来决定,将会在后面进行讨论。
经过多次混合模式的垃圾收集之后,很多老年代region其实已经处理过了,然后G1又切换回纯年轻代模式,直到下一次的并发标记周期完成。

4. 标记周期的各个阶段

G1的标记周期包括以下这些阶段:

【初始标记阶段】(Initial mark phase): 在此阶段标记 GC roots, 一般是附加在某次常规的年轻代GC中顺带着执行。【扫描GC根所在的region】(Root region scanning phase): 根据初始标记阶段确定的GC根元素,扫描这些元素所在region,获取对老年代的引用,并标记被引用的对象。 该阶段与应用线程并发执行,也就是说没有STW停顿,必须在下一次年轻代GC开始之前完成。【并发标记阶段】(Concurrent marking phase)”: 遍历整个堆,查找所有可达的存活对象。 此阶段与应用线程并发执行, 也允许被年轻代GC打断。【再次标记阶段】(Remark phase): 此阶段有一次STW暂停,以完成标记周期。 G1会清空SATB缓冲区,跟踪未访问到的存活对象,并进行引用处理。【清理阶段】(Cleanup phase): 这是最后的子阶段,G1在执行统计和清理RSet时会有一次STW停顿。 在统计过程中,会把完全空闲的region标记出来,也会标记出适合于进行混合模式GC的候选region。 清理阶段有一部分是并发执行的,比如在重置空闲region并将其加入空闲列表时。 5. 常用参数与默认值

G1是一款自适应垃圾收集器,大部分的参数都有默认值,一般情况下无需太多配置即可高效运行。
下面列出常用参数和对应的默认值, 如果有特殊需求,可调整JVM启动参数,以满足特定的性能指标。

-XX:G1HeapRegionSize=n

用来设置G1 region 的大小。 必须是2的幂(x次方),允许的范围是 1MB 至 32MB。
这个参数的默认值, 会根据堆内存的初始大小(-Xms)与最大值(-Xmx)动态调整,以便将堆内存切分为2048个左右的region。

-XX:MaxGCPauseMillis=200

期望的最大暂停时间。 默认值为200毫秒。 这个值不会自动调整,启动时设置为多少就是多少。

-XX:G1NewSizePercent=5

设置年轻代的最小空间占比, 默认值为5,相当于最少有5%的堆内存会作为年轻代来使用。
这个参数会覆盖 -XX:DefaultMinNewGenPercent。
这是实验性质的参数,后续版本有可能会有变更。

-XX:G1MaxNewSizePercent=60

设置年轻代的最大空间占比。 默认值为60,相当于最多有60%的堆内存会作为年轻代来使用。
此设置会覆盖 -XX:DefaultMaxNewGenPercent。
这是实验性质的参数,后续版本有可能会有变更。

-XX:ParallelGCThreads=n

设置STW阶段的并行worker线程数。

如果逻辑处理器小于等于8个,则默认 n 等于逻辑处理器的数量。如果逻辑处理器大于8个,则 n 默认约等于处理器数量的5/8 + 3。如果是高配置的 SPARC 系统,则默认 n 大约等于逻辑处理器数量的5/16。大多数情况下使用默认值即可。有一种情况除外,就是Docker容器中使用了低版本JDK,案例参考: JVM 问题排查分析下篇(案例实战)。 -XX:ConcGCThreads=n

设置并发标记的GC线程数。 默认值约等于 ParallelGCThreads 值的 1/4。

-XX:InitiatingHeapOccupancyPercent=45

设置标记周期的触发阈值, 即Java堆内存使用率的百分比。 默认的触发阈值是整个Java堆的45%。

-XX:G1MixedGCLiveThresholdPercent=65

执行混合模式GC时,根据老年代region的使用率,确定是否包含到回收集之中。 阈值默认为65%。
此设置会覆盖 -XX:G1OldCSetRegionLiveThresholdPercent。
这是实验性质的参数,后续版本有可能会有变更。

-XX:G1HeapWastePercent=10

设置可以容忍的堆内存浪费率百分比。
如果可回收的堆内存占比小于这个阈值比例,则 HotSpot 不会启动混合模式GC。
默认值为10%。

-XX:G1MixedGCCountTarget=8

在标记周期完成后,期望执行多少次混合模式的GC,直到存活数据的比例降到 G1MixedGCLiveThresholdPercent 之下。
默认是执行8次混合模式的GC。 具体执行的次数一般都会小于这个值。

-XX:G1OldCSetRegionThresholdPercent=10

混合模式的GC中,每次处理的老年代 region 数量上限占比。 默认值为Java堆的10%。

-XX:G1ReservePercent=10

设置一定比例的保留空间, 让其保持空闲状态,降低 to空间 内存不足的风险。 默认值为 10%。
虽然这是一个百分比,但实际会映射为具体的大小,所以当增加或减少百分比时,最好将Java堆的总大小也进行同样大小的调整。

6. 如何解锁实验性质的JVM参数

要修改实验性质的JVM参数值,必须先进行声明。
我们可以在命令行参数中,设置实验性质的参数之前,明确指定 -XX:+UnlockExperimentalVMOptions。 例如:

java -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=10 -XX:G1MaxNewSizePercent=75 G1test.jar 7. 最佳实践与建议

调整G1参数之前,需要记住以下几点:

禁止设置年轻代的大小: 不要使用 -Xmn、-XX:NewRatio 之类的选项来指定年轻代的大小。 如果指定固定的年轻代大小,则会覆盖最大暂停时间目标,可以说得不偿失。期望的最大暂停时间值: 不管对哪一款垃圾收集器进行调优,都需要在延迟与吞吐量指标之间进行权衡。
G1是一款具有统一暂停时间的增量式垃圾收集器, 所以对CPU资源的开销相对要大一些。 G1的吞吐量目标,是指在 高负载 场景下,确保应用线程占有90%以上的CPU时间,GC线程的开销保持在10%以下。
相比之下,HotSpot中自带的高吞吐量垃圾收集器可以优化到 99% 的应用线程时间, 也就是说只有不到1%的GC开销。
因此,在压测G1的吞吐量指标时,需要放宽暂停时间指标。 如果设定的暂停时间目标值太小,就表示你愿意承担较大的GC开销,但这会影响到吞吐量。 在压测 G1 的延迟指标时,可以设置期望的软实时暂停时间指标,G1会尽力达成此目标。 副作用则是吞吐量会受到影响。对大部分服务端应用程序来说,CPU负载不会超过50%,即使GC多占了一点CPU也影响不大,因为还有很多冗余, 我们更关注的是GC暂停时间,因为这关系到响应延迟指标。混合模式的GC: 在调优混合模式的GC时,可以尝试以下选项。 这些选项的详细信息请参考前面的小节: -XX:InitiatingHeapOccupancyPercent: 设置标记周期的触发阈值。-XX:G1MixedGCLiveThresholdPercent 和 -XX:G1HeapWastePercent: 调整混合模式GC相关的策略。-XX:G1MixedGCCountTarget 和 -XX:G1OldCSetRegionThresholdPercent 用于优化调整CSet中的老年代region比例。 8. GC日志中内存溢出和内存耗尽的信息

如果我们在GC日志中看到 to-space overflow/exhausted, 则表明G1没有足够的内存来存放存活区或者需要提升的对象,或者两者都不足。 这时候Java堆内存一般都已达到最大值,无法自动扩容。 示例如下:

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 选项的值,增加并发标记的线程数。

这些选项的具体信息,请参考前面的描述。

9. 大对象/巨型对象的内存分配

如果某个对象超过单个 region 空间的一半,则会被G1视为 【大对象/巨型对象】(Humongous object)。 例如一个很大的数组或者String。
这样的对象会直接分配到老年代的 “大对象region区(Humongous region)”。 一个大对象region区就是一组虚拟地址空间连续的region。 StartsHumongous 标志着开头的region,而 ContinuesHumongous 则标记随后的region集合。

在分配大对象region区之前,G1会先判断是否达到开启标记周期的阈值,在必要时会启动并发标记周期。

在标记周期最后的清理阶段,以及FullGC的清理过程中,都会释放不再使用的巨型对象。

为了减少内存复制的开销,所有转移暂停GC都不进行巨型对象的压缩和整理。 Full GC 时才会将巨型对象整理到位。

由于每个 StartsHumongous 和 ContinuesHumongous 组成的集合中都只保存一个巨型对象, 因此这个组合内部,最后面的空间总有一部分是浪费的。
如果某个对象占用的空间,只比N个region大上那么一点点,那么未使用的那部分空间实际上就产生了内存碎片。

如果在GC日志中,看到由 Humongous 分配而触发的大量并发周期,而且在老年代中形成了大量的内存碎片,就需要加大 -XX:G1HeapRegionSize 的值,让之前的巨型对象不再被当成巨无霸,而是走常规的对象分配方式【只要其小于region的50%即可】。

10. 总结

G1是一款 【并行+并发】 方式的【增量】垃圾收集器,将堆内存划分为很多个region,与其他 GC 算法实现相比,提供了可预测性更精准的暂停时间。
增量特性使得G1可以处理更大的堆内存空间,在最坏情况下依然保持合理的响应时间。

G1具有自适应特性,一般情况下,只需要设置3个调优参数即可:

期望的最大暂停时间, 例如 -XX:MaxGCPauseMillis=50堆内存的最大值, 例如 -Xmx4g堆内存的最小值, 例如 -Xms4g 作者简介

Monica Beckwith, Oracle技术工作组的核心成员,是Java HotSpot VM 项目下, Garbage First Garbage Collector 的性能负责人。
在性能和架构领域具有10年以上的工作经验。
在Oracle和Sun Microsystems之前的工作,Monica 负责 Spansion Inc.的性能调优工作。
Monica与许多基于Java的性能测试标准进行了合作, 致力于探寻 Java HotSpot VM 的性能改进。

相关资源和链接

G1生产环境参数调优-EN

垃圾优先型垃圾回收器调优-官方的机器翻译中文版

G1垃圾收集器调优-英文版

JavaSE官方文档目录-英文版

G1特性简介-英文版

HotSpot垃圾收集器简介-英文版

Java Hotspot G1 GC的一些关键技术

Understanding G1 GC Logs

Facebook: I Love Java

Twitter: Java

Java Blog

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。