java gc横向测评,如何选择合适的GC策略?

java

GC

分代假设:大部分新生对象很快无用,存活较长时间的对象,可能存活更长时间。经历了15次GC还存在的就放在老年代.新生代80%,S0,S1各10%新生代到存活区是复制,到老年代是移动。

-XX: +MaxTenuringThreshold=15 表示15次后放到老年代。

可以做为GC ROOT的对象

  1. 当前正在执行的方法里的局部变量和输入参数
  2. 活动线程
  3. 所有类的静态字段
  4. JNI引用
Serial GC/ParNewGC

-XX: +UseSerialGC开启

对年轻代使用mark-copy算法,对老年代使用mark-sweep-compact算法

串行GC不能并行处理,所以触发全部暂停(STW)

ParNewGC可以配合CMSGC使用

适用场景:

  • 单线程应用或资源有限的环境(如嵌入式系统)。
  • 小型应用,不需要频繁的垃圾回收。

java版本19,测试GC效率

命令

java -XX:+UseSerialGC -Xms128m -Xmx128m -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

效果。可以看到当分配128m内存的时候,频繁触发GC,最终还是OOM,说明堆大小设置的太小了。
在这里插入图片描述

接下来试试512m堆大小

java -XX:+UseSerialGC -Xms512m -Xmx512m -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

可以看到效果,前面几次yong GC后,触发了很多次full GC,最终成功运行,但是时间很长
在这里插入图片描述

再试试1G内存呢

java -XX:+UseSerialGC -Xms1g -Xmx1g -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

可以看到GC次数大大减少,效率提升,512m的时候生成了30000多个对象,1g的时候生成了33000多个对象

在这里插入图片描述

接下来试试2g内存

java -XX:+UseSerialGC -Xms2g -Xmx2g -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

可以看到GC次数减少了一半,生成了36000多个对象。性能再次提升

在这里插入图片描述

Parallel GC

目标是最大化应用程序运行时间(吞吐量),最小化 GC 时间。还是会短暂的暂停业务。需要业务能接受短暂的暂停。

-XX: +UseParallelGC
-XX: +UseParallelOldGC 这个在java9以上已经被移除

年轻代和老年代GC都会触发STW事件。

对年轻代使用mark-copy算法,对老年代使用mark-sweep-compact算法

-XX: ParallelGCThreads=N 来指定GC线程数,默认值为CPU核心数。

优点:

  • 多线程回收显著提高了回收效率,适合多核环境。
  • 停顿时间较 Serial GC 短。
  • 两次GC之间不消耗系统资源。

缺点:

  • GC 停顿仍然是全暂停(STW)。
  • 不适合对延迟要求苛刻的场景。

适用场景:

  • 需要高吞吐量的大型后台任务(如批处理、数据分析)。
  • 多核 CPU 环境。

接下来试试并行GC 128m内存

java -XX:+UseParallelGC -Xms128m -Xmx128m -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

结果同样OOM,但是仅仅GC了16次,串行GC了25次,花了0.162s才OOM,而并行GC花了0.108s

在这里插入图片描述

接下来试试512m

java -XX:+UseParallelGC -Xms512m -Xmx512m -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

可以发现GC次数大大增加了,吞吐量也降低了。27000多个对象,99次GC。

在这里插入图片描述

再来试试1g内存

java -XX:+UseParallelGC -Xms1g -Xmx1g -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

跟串行GC相比,GC次数增加,但是吞吐量也增加了很多,串行GC生成了33000多个对象,而并行GC生成了50000多个对象。有明显提升。

在这里插入图片描述

在试试2g内存

java -XX:+UseParallelGC -Xms2g -Xmx2g -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

可以发现跟1g内存相比,GC次数大大减少,但是吞吐量并没有明显增加。仅仅生成了55000多个对象。但是性能依然比串行GC强大

在这里插入图片描述

CMS GC

-XX: +UseConcMarkSweepGC

对年轻代使用STW的mark-copy算法,对老年代主要使用并发的mark-sweep算法。这个GC已经在java14版本被移除了。

设计目标:专为老年代设计,目标是最小化 GC 停顿时间。

  1. 不对老年代进行整理,而是使用空闲列表来管理内存空间的回收
  2. 在mark-and-sweep的工作和业务线程并发执行。

默认并发线程数等于CPU核心数的1/4

6个阶段

  1. 初始标记
  2. 并发标记
  3. 并发预清理
  4. 最终标记
  5. 并发清楚
  6. 并发重制

MaxHeapSize是系统的1/4内存
MaxNewSize是MaxHeapSize的1/3
NewSize是系统的1/64

优点:

  • 只有yongGC暂停业务。GC 停顿时间短,适合延迟敏感的应用。
  • 并发回收利用多核资源减少 STW 时间。

缺点:

  • 内存碎片化:CMS 不会整理内存,可能导致分配大对象失败(触发 Full GC)。
  • CPU 开销较高:并发阶段可能与用户线程争抢资源。
  • 容易产生 “Concurrent Mode Failure”:若老年代空间不足,回退到 Serial GC。

适用场景:

  • 延迟敏感的应用(如 Web 服务、在线交易系统)。
  • 多核环境下的中大型应用。
G1 GC

分区堆内存:将堆划分为若干独立的固定大小的区域(Region),每个 Region 可充当年轻代、老年代或其他用途。混合回收:通过优先回收包含最多垃圾的 Region(Garbage-First)。并行和并发回收:减少 STW 时间。内置碎片整理机制,避免了 CMS 的碎片化问题。一般一个Region是1M。一部分 Region 保留为 Humongous(H) 区,用于存储超过单个 Region 大小一半的巨型对象。

-XX: +UseG1GC -XX: MaxGCPauseMillis=50

将STW的时间和分布变成可预期和可配置的,可设置某项特定的性能指标,为了达成可预期的指标,有独特的实现。增量方式,每次处理一部分,称为回收集合,每次处理所有的年轻代和部分老年代。能看到哪个块的垃圾多,优先回收他们

处理步骤

  1. 年轻代模式转移暂停
  2. 并发标记
  3. 转移暂停:混合模式

G1GC可能退化成串行GC

  1. 并发模式失败:增加堆大小
  2. 晋升失败:
  3. 巨型对象分配失败:增加内存或增大Region大小

优点:

  • 减少内存碎片化。
  • 更好地控制 GC 停顿时间,可通过 -XX:MaxGCPauseMillis 调整。
  • 自动调节年轻代和老年代的大小。

缺点:

  • 实现复杂,配置选项多。
  • 内存占用较高,CPU 开销大。

适用场景:

  • 需要低延迟的中大型应用。
  • 堆内存较大的环境(如 >4GB)。
  • 替代 CMS 的推荐选择。

Mixed GC

  • 老年代内存使用率达到一定阈值(默认 45%,可通过 -XX:InitiatingHeapOccupancyPercent 调整)。
  • 同时回收年轻代和部分老年代。
  • 执行
    • 标记 GC Roots 直接引用的对象。
    • 并发扫描老年代,标记存活对象。
    • 处理标记期间新产生的引用变化。
  • 根据垃圾优先(Garbage-First)的原则,优先选择包含最多垃圾的 Region。

接下来试试G1 GC 128m内存

java -XX:+UseG1GC -Xms128m -Xmx128m -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

同样GC了103次后发生了OOM,可以看到GC次数很多。时间也很长,花了0.136s
在这里插入图片描述

接下来试试512m内存

java -XX:+UseG1GC -Xms512m -Xmx512m -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

可以看到GC了600多次,才生成了29000多个对象。效率很低。但是比并行GC还是要好一些。

在这里插入图片描述

接下来试试1g内存

java -XX:+UseG1GC -Xms1g -Xmx1g -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

可以发现效率大大提升,生成了56000多个对象。GC了174次。其吞吐量相比并行GC稍微提升,GC次数却很多,说明GC期间很少影响业务。
在这里插入图片描述

再试试2g内存

java -XX:+UseG1GC -Xms2g -Xmx2g -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

可以看到GC次数减少,但是性能并没有什么提升,这也说明了GC对于性能的影响开始减少
在这里插入图片描述

ZGC

专注于低延迟,目标是将 GC 停顿时间控制在 10ms 以下。支持非常大的堆内存(TB 级)。使用多线程并发回收,避免长时间的 STW。基于标记-整理算法,避免内存碎片化。使用指针染色(Pointer Coloring)来实现并发标记和引用更新。

-XX: +UnlockExpermentalVMOptions
-XX: +UseZGC
-XX: -Xmx16g

优点:

  • 极低的 GC 停顿时间。
  • 支持超大堆,扩展性好。
  • 减少内存碎片。
  • 与G1相比,应用吞吐量下降不超过15%

缺点:

  • 内存占用较高:由于需要染色指针和写屏障。
  • 不适合资源紧张的环境。

-XX: +UseShenandoahGC

立项比ZGC早,暂停时间与堆大小无关

适用场景:

  • 延迟敏感的大型应用(如金融交易、高并发系统)。
  • 超大堆应用(TB 级别内存)。

接下来试试 ZGC 128m内存

java -XX:+UseZGC -Xms128m -Xmx128m -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

可以看到GC12次就出现了OOM,花了0.157s。和并行GC接近,比G1GC的GC次数少
在这里插入图片描述

试试512m内存

java -XX:+UseZGC -Xms512m -Xmx512m -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

79次GC,生成了35000多个对象。可以看到效果比G1GC要好一些。
在这里插入图片描述

再看看1g内存

java -XX:+UseZGC -Xms1g -Xmx1g -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

结果22次GC,生成了57000多个对象,GC次数相比G1减少了很多,性能也差不多。
在这里插入图片描述

再试试2g内存

java -XX:+UseZGC -Xms2g -Xmx2g -XX:+PrintGCDetails -Xlog:gc\*:file=gc.log:time,uptime,level,tags GCLogAnalysis

结果是9次GC,生成了54000多个对象
在这里插入图片描述

Shenandoah GC

目标是极低延迟,尽可能减少 STW 时间。采用并发压缩技术(Concurrent Compaction),避免碎片化问题。大部分回收工作与应用线程并发执行。

优点:

  • GC 停顿时间低。
  • 内存整理避免碎片化。
  • 对堆内存的需求比 ZGC 更低。

缺点:

  • 较新的 GC,生态和调优支持可能不如 G1 和 ZGC。
  • CPU 开销高。

适用场景:

  • 延迟敏感的应用。
  • 中大型堆内存的场景。
GC总结

从GC的发展历史来看,目标是两个

  1. 尽可能减少停顿时间,不影响业务。
  2. 尽可能减少GC时间,让GC更快。

通过以上的对比可以发现,堆的大小不能设置的太小导致OOM,同样当堆设置的过大对于性能也并没有什么提升,比如2g对比1g内存,就没什么提升。从512m和1g内存对比上来看,ZGC最优,不仅减少了GC次数,性能也很好。而G1GC的优化最好,因为它的GC次数最多,但是性能同样强大。

从结果来看最好的是ZGC,其次可以选择G1GC或者并行GC。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值