G1 垃圾回收机制
一、序言
在我之前的文章中介绍过常见的垃圾回收算法,那些都只是理论,并没有真正实现一个垃圾回收器。而常见的本章就简单介绍几种,本章的重点是介绍G1垃圾回收。本章图片均来源于网络
二、常见的垃圾回收器
目前主流的垃圾回收器有8种,例如下图:
上述图片来源于网络
- 新生代可以适用的垃圾回收器:Serial(串行收集器)、ParNew(并行年轻代收集器)、Parallel Scavenge(并行回收器)
- 老年代可以适用的垃圾回收器:CMS(并发标记-清除收集器)、Serial Old(串行老年代收集器)、Parallel Old(并行老年代收集器)
- G1(Garbage First 收集器)和 ZGC(Z Garbage Collector)回收器适用于新生代和老年代混合回收
- 相互之间有连线的表示可以配合使用,常用组合:Serial+Serial Old, Parallel Scavenge+Parallel Old,ParNew+CMS,G1(不需要组合其他收集器)
名词解释:
并行回收(Parallel):多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
串行回收:单个垃圾收集线程工作
Minor GC(Young GC):发生在年轻代的GC
Full GC:发生在老年代的GC,full GC回收速度一般比Minor GC慢10倍左右,JVM调优也是主要针对Full GC进行调优
三、G1回收器
一:介绍
G1(Garbage-First)垃圾回收机制是 JDK 7u4版 引入的一种新的垃圾回收器,并且在 JDK 9 中成为默认的垃圾回收,旨在解决传统垃圾回收器在处理大规模堆内存时的性能问题。
在我的 后端(五):JVM_后端程序员要会jvm吗-CSDN博客 这一篇文章中解释到:
分代回收是将内存分为 eden 区,survivor 区,old 区 。而G1则是沿用了这三个区,并且添加了一个 humongous 区,这个区域专门存放 大对象。
- 分代回收算法中,默认情况下新生代的 Eden 区和两个 Survivor 区的空间比例是 8:1:1。也就是说,Eden 区占用新生代约 80% 的空间,每个 Survivor 区各占 10%。
- 老年代和新生代的默认比例并没有固定的标准,通常新生代占堆内存的 1/3 到 1/4,老年代则占 2/3 到 3/4。例如,当堆内存为 3GB 时,新生代可能分配 1GB,老年代分配 2GB。
G1 算法将整个堆划分为 很多个大小相同的区域,每隔区域都可以作为 伊甸园区、幸存者区和老年代。
二:年轻代的垃圾回收
- 最开始都是空闲的。当创建了对象时,就挑出一些空闲的区域作为伊甸区。
- 随着对象的不断创建,这个伊甸园区,很快就会被填满,就会触发一个新生代的垃圾回收。【新生代所占用的仅是这个堆中的一小部分,在 5%~6% 之间波动, Young Collection(年轻代垃圾回收)会自动的进行调整。】
- 此时,会触发一次垃圾回收,这个回收呢,也是通过可达性分析进行查找垃圾的,把存活下来的对象进行标记,随后通过复制算法将其复制到幸存者区。
如下图所示:
其他 E(Eden)区释放。但是在这个删除过程中呢,还是会触发一次 STW ,这个时间很短(这个对象少)。
随着时间的流逝,伊甸区的内存又不足了。
重复上述操作,再次将伊甸区存活的对象和 S 当中存活的对象移动到另一个幸存者区。当然在这个过程中,如果有对象超过了晋升阈值,就会存放到老年代中。
如下图:
三:混合收集阶段
年轻代垃圾回收+并发标记
Young Collection + Concurrent Mark
- 这个阶段有个触发条件,就是当老年代的内存越来越多,超过一个阈值,这个阈值是 45%以上,就会触发这个并发标记。
- 在这个老年代中找到存活的对象并且给他们加上标记,这个过程还是并发标记的,不会暂停其他线程,但是他需要处理那些漏标的对象,所以还是会有 STW 问题需要用户暂停。
- 这些都完成之后就知道了老年代有哪些存活的对象,随后进入混合收集阶段,此时不会对所有的老年代进行垃圾回收,而是根据 暂停时间目标 优先回收价值量高的(存货对象少)的区域(这也是Gabage First 名称的由来)。
- 在混合收集阶段,并非一下子收集所有的老年代中的内存(如果这么操作了,可能暂停的时间就会很长),达不到我们预期的暂停时间。为了让它不超过这个预期的暂停时间,就优先考虑哪些回收价值高的对象,如上图中的 红色 老年代(存活对象少)
- 在进行GC 时,新生代还是和之前一样,但是老年代中,将存活的对象和幸存者中达到阈值的对象收集到一个新的老年区中,将参与的老年代全部删除。
如下图所示:
一次老年代 GC 后的结果:
这里可能会需要多次删除操作。
如果一块区域不够大的话,会分配一个连续的区域来存储巨型对象(humongous)。
并发模式失败
在 G1 进行并发标记阶段时,需要在应用程序运行的同时标记出存活对象。如果在这个过程中,老年代的空间被快速填满,没有足够的空间容纳新晋升的对象或者并发标记还未完成就需要进行垃圾回收,就会导致并发模式失败。此时,G1 会放弃并发标记过程,触发 Full GC 来清理整个堆。