Java进阶指南:GC 算法2.3CMS GC/ G1 GC

系列文章目录

1:JVM核心知识      

        1.1:字节码

        1.2:类加载器

        1.3:内存模型

        1.4:JVM启动参数

        1.5:相关性能分析工具

2.GC算法

        2.1:GC的背景与一般原理

        2.2:串行GC与并行GC

        2.3:CMS GC/ G1 GC


目录

系列文章目录

前言

二、CMS GC的六个阶段

阶段 1: Initial Mark(初始标记)

阶段 2: Concurrent Mark(并发标记)

阶段 3: Concurrent Preclean(并发预清理)

阶段 4: Final Remark(最终标记)

阶段 5: Concurrent Sweep(并发清除)

阶段 6: Concurrent Reset(并发重置)

1.初始标记

2.并发标记

3.并发预清理

4.最终标记

5.并发清除

6.并发重置

三、CMS GC的优点与坑

四、G1 GC

五、G1 GC的处理步骤

六、G1 GC 的注意事项

总结



前言

本系列主要针对想进一步进阶Java的开发者,本文介绍CMS GC与G1 GC


一、什么是CMS GC?

CMS GC(Mostly Concurrent Mark and Sweep Garbage Collector)

意为最大并发的标记清除GC

-XX:+UseConcMarkSweepGC来使用

其对年轻代采用并行 STW 方式的 mark-copy (标记-复制)算法,对老年代主要使用并发 mark-sweep ( 标记-清除)算法,注意这里对老年代的处理与前面的串行GC和并行GC不一样。

CMS GC 的设计目标是避免在老年代垃圾收集时出现长时间的卡顿(因为并行GC虽然吞吐量高,但是所有的CPU都用来做GC,单次GC暂停时间很长,对业务影响较大),主要通过两种手段来达成此目标:

1. 不对老年代进行整理,而是使用空闲列表(free-lists)来管理内存空间的回收。这里的空闲列表相当于一个索引,索引上面记录了当前所有可用内存的空间位置。

2. 在 mark-and-sweep (标记-清除) 阶段的大部分工作和应用线程一起并发执行

也就是说在多核CPU的情况下,如果想降低系统的延迟,可用CMS GC。这里再提一点,parallel是GC过程中占用了所有的CPU,而Concurrent是CPU同时处理GC与业务线程。

二、CMS GC的六个阶段

这里先说结论,CMS GC是把对老年代的标记的过程分成了几个小段(与并行GC对年轻代的处理是相同的),把一次比较长的标记暂停分摊了,所以对业务的影响会更小一点。

阶段 1: Initial Mark(初始标记)

阶段 2: Concurrent Mark(并发标记)

阶段 3: Concurrent Preclean(并发预清理)

阶段 4: Final Remark(最终标记)

阶段 5: Concurrent Sweep(并发清除)

阶段 6: Concurrent Reset(并发重置)

1.初始标记

这个阶段伴随着 STW 暂停。初始标记的目标是标记所有的 根对象,包括根对象直接引用的对象,以及被年轻代中所 有存活对象所引用的对象(老年代单独回收)。

什么意思呢,就是只标记GC roots,然后标记老年代中被年轻代直接引用的,年轻代在这一阶段是没有标记对象的。可以认为这个初始标记就是泛泛的标记了一下。

2.并发标记

在此阶段,CMS GC 遍历老年代,标记所有的存活对象,从 前一阶段 “Initial Mark” 找到的根对象开始算起。 “并发标 记”阶段,就是与应用程序同时运行,不用暂停的阶段

这个阶段会动态标记老年代中的对象,还是与年轻代无关

3.并发预清理

此阶段同样是与应用线程并发执行的,不需要停止应用线 程。 因为前一阶段【并发标记】与程序并发运行,可能 有一些引用关系已经发生了改变。如果在并发标记过程中 引用关系发生了变化,JVM 会通过“Card(卡片)”的方 式将发生了改变的区域标记为“脏”区,这就是所谓的 卡片 标记(Card Marking)

4.最终标记

最终标记阶段是此次 GC 事件中的第二次(也是最后一次)STW 停顿。本阶段的目标是完成老年代中所有存活对象的标记。因为 之前的预清理阶段是并发执行的,有可能 GC 线程跟不上应用程 序的修改速度。所以需要一次 STW 暂停来处理各种复杂的情况。 通常 CMS 会尝试在年轻代尽可能空的情况下执行 Final Remark 阶段,以免连续触发多次 STW 事件。

5.并发清除

此阶段与应用程序并发执行,不需要 STW 停顿。JVM 在此 阶段删除不再使用的对象,并回收他们占用的内存空间。

6.并发重置

此阶段与应用程序并发执行,重置 CMS 算法相关的内部 数据,为下一次 GC 循环做准备。

三、CMS GC的优点与坑

CMS 垃圾收集器在减少停顿时间上做了很多复杂而有用的 工作,用于垃圾回收的并发线程执行的同时,并不需要暂停应用线程。

当然,CMS 也有一些缺点,其中最大的问题就 是老年代内存碎片问题(因为不压缩),在某些情况下 GC 会造成不可预测的暂停时间,特别是堆内存较大的情况下。

也就是说在我们线上系统的运行过程中,这个内存使用会逐渐增大(由内存碎片所导致),等达到一定程度时,CMS GC对老年代的优化会经常执行,使得性能骤降。

四、G1 GC

G1 的全称是 Garbage-First,意为垃圾优先,哪一块的垃圾最 多就优先清理它。

G1 GC 最主要的设计目标是:将 STW 停顿的时间和分布,变成可预期且可配置的。

其实G1 GC的设计思路是增量设计,就是我把整个内存分成一小部分一小部分,每次处理的块小了很多,当然处理的过程会更快。

-XX:+UseG1GC -XX:MaxGCPauseMillis=50

那么,G1 GC是如何实现的呢?

首先,堆不再分成年轻代和老年代,而是划分为多个(通常是 2048个)可以存放对象的小块堆区域(smaller heap regions)。 每个小块,可能一会被定义成 Eden 区,一会被指定为 Survivor 区或者Old 区。在逻辑上,所有的 Eden 区和 Survivor 区合起来 就是年轻代,所有的 Old 区拼在一起那就是老年代。

每次G1 GC的过程中,会收集所有的年轻代,但是只会用一小部分的老年代,而且G1 GC在并发阶段估算每个小堆 块存活对象的总数。构建回收集的原则是: 垃圾最多的小块会被优先收集。这也是G1(garage first)名称的由来

五、G1 GC的处理步骤

1.年轻代模式转移暂停(Evacuation Pause)

G1 GC 会通过前面一段时间的运行情况来不断的调整自己的回收策略和行为,以此来比较稳定地控制暂 停时间。

在应用程序刚启动时,G1 还没有采集到什么足够的信息,这时候就处于初始的 fully-young 模式。当年轻代空间用满后,应用线程会被暂停,年轻代内存块中的存活对象被拷贝到存活区。

如果还没有存活区,则任意选择一部分空闲的内存块作为存活区。

拷贝的过程称为转移(Evacuation),这和前面介绍的其他年轻代收集器是一样的工作原理。

2.并发标记(Concurrent Marking)

与CMS GC类似,

不过在初始标记的时候采用的是快照的方式,而不需要进行一次STW,在标记阶段开始时记下所有的存活对象。即使在标记的同时又有一些变成了垃圾。通过对象的存活信息,可以构建出每个小堆块的存活状态,以便回收集能高效地进行选择。

而在最终标记的时候与CMS类似,也会进行一次STW

注意还有一次STW,是在清理的时候,这里的清理,不是清理对象,而是回收没有对象的内存块,并做一些统计数据来方便接下来清理对象的操作。

3.转移暂停: 混合模式(Evacuation Pause (mixed))

之所以叫混合模式,是因为不只清理年轻代,还将一部分老年代区域也加入到回收集中。

这里的转移可能不会紧接着并发标记,因为在并发标记的最后一个阶段会进行一次内存回收,如果这次回收效果很棒,那么就没有必要立刻转移。

这意味着,在正式进行一次转移暂停的时候,可能会进行多次年轻模式的转移暂停。

看起来和我们之前介绍的GC 过程有很大不同,实际上还是类似,因为年轻模式的转移暂停就对应着年轻代GC,混合模式的转移暂停就对应着老年代GC,这个并发标记实际上是老年代GC的一个前置步骤。

六、G1 GC 的注意事项

G1 GC可能在一些情况下会产生full GC ,退化成串行GC,会有较大的时间暂停。

核心关键就是内存的问题

1.G1 启动标记周期,但在 Mix GC 之前,老年代就被填满,这时候 G1 会放弃标记周期

这是什么意思呢,就是说老年代被填满了,没有用到G1 GC算法中的并发标记的优势,所以只能串行处理。我们可以通过增加GC的频率来防止在两次GC之间老年代过早的被填满。

核心参数:增加线程数-XX:ConcGCThreads

2.晋升失败

没有足够的内存供存活对象或晋升对象使用,由此触发了 Full GC

导致这个问题的原因是我们在转移的过程中,没有足够的内存了,解决的思路也是提高GC的频率。

1.可以通过增加–XX:G1ReservePercent 选项的值来提高预留内存大小,预留内存大小变大,那么年轻代出发GC就会更容易,GC效率就会增加。

2.也可以通过减少 –XX:InitiatingHeapOccupancyPercent 提前启动标记周期,提高GC频率。

3.巨型对象分配失败

当巨型对象找不到合适的空间进行分配时,就会启动 Full GC,来释放空间。

解决办法:增加内存或者增大 -XX:G1HeapRegionSize


 

总结

以上就是今天要讲的内容,本文介绍了CMS GC 与 G1 GC 的原理与一些坑,下面我们将介绍ZGC/Shenandoah GC。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Windows系统中,当Java应用程序尝试进行垃圾回收(GC)时,如果花费的时间过长,就会引发"java.lang.OutOfMemoryError: GC overhead limit exceeded"异常。这个错误通常表示垃圾回收器花费了过多的时间,但仍无法回收足够的内存空间来支持应用程序的正常运行。 GC overhead limit exceeded错误通常是由于应用程序的内存使用过高或垃圾回收算法配置不当导致的。为了解决这个问题,可以尝试以下几个方法: 1. 增加Java虚拟机(JVM)的堆内存大小:可以通过设置JVM参数-Xmx和-Xms来增加堆内存的大小。例如,可以将-Xmx参数设置为较大的值(例如2GB)来提供更多的内存空间供应用程序使用。 2. 优化代码和内存使用:检查应用程序的代码,查找可能导致内存泄漏或过度使用的部分,并进行优化。确保及时释放不再使用的对象和资源,避免创建过多的临时对象。 3. 调整垃圾回收器参数:可以尝试调整垃圾回收器的配置参数,以适应应用程序的需求。可以尝试使用不同的垃圾回收器算法(例如CMSG1等)或调整各种垃圾回收器参数(例如堆大小、回收器线程数等)。 4. 分析堆转储文件:当发生OutOfMemoryError时,可以生成堆转储文件(Heap Dump),并使用工具来分析该文件,以了解内存使用情况和可能的内存泄漏问题。这可以帮助定位和解决内存相关的问题。 需要注意的是,解决java.lang.OutOfMemoryError: GC overhead limit exceeded错误需要综合考虑应用程序的具体情况和需求,可能需要多次尝试不同的方法和参数配置才能找到最佳解决方案。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值