写点什么

说一下 jvm 有哪些垃圾回收器?(1),java 异常面试题

用户头像
极客good
关注
发布于: 刚刚

1、G1 垃圾收集器

把 G1 单独拿出来的原因是其比较复杂,在 JDK 1.7 确立是项目目标,在 JDK 7u2 版本之后发布,并在 JDK 9 中成为了默认的垃圾回收器。通过“-XX:+UseG1GC”启动参数即可指定使用 G1 GC。


G1 从整体看还是基于标记-清除算法的,但是局部上是基于复制算法的。这样就意味者它空间整合做的比较好,因为不会产生空间碎片。G1 还是并发与并行的,它能够充分利用多 CPU、多核的硬件环境来缩短“stop the world”的时间。G1 还是分代收集的,但是 G1 不再像上文所述的垃圾收集器,需要分代配合不同的垃圾收集器,因为 G1 中的垃圾收集区域是“分区”(Region)的。G1 的分代收集和以上垃圾收集器不同的就是除了有年轻代的 ygc,全堆扫描的 full?GC 外,还有包含所有年轻代以及部分老年代 Region 的 Mixed?GC。G1 还可预测停顿,通过调整参数,制定垃圾收集的最大停顿时间。


G1 收集器的运作大致可以分为以下步骤:初始标记、并发标记、最终标记、筛选回收。其中初始标记阶段仅仅只是标记一下 GC Roots 能直接关联到的对象,并且修改 TAMS(Next Top at Mark Set)的值,让下一个阶段用户程序并发运行时,能在正确可用的 Region 中创建新对象,这个阶段需要 STW,但耗时很短。并发标记阶段是从 GC Roots 开始对堆中对象进行可达性分析,找到存活的对象,这阶段耗时较长,但是可以和用户线程并发运行。最终标记阶段则是为了修正在并发标记期间因用户程序继续运行而导致标记产生变化的那一部分标记记录,虚拟机将这段时间对象变化记录在线程 Remembered Set Logs 里面,最终标记需要把 Remembered Set Logs 的数据合并到 Remembered Sets 中,这阶段需要暂停线程,但是可并行执行。最后的筛选回收阶段首先对各个 Region 的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来确定回收计划。G1 收集器运行示意图如下图所示。


2、G1 分区的概念

G1 的堆区在分代的基础上,引入分区的概念。G1 将堆分成了若干 Region,以下和”分区”代表同一概念。(这些分区不要求是连续的内存空间)Region 的大小可以通过 G1HeapRegionSize 参数进行设置,其必须是 2 的幂,范围允许为 1Mb 到 32Mb。 JVM 的会基于堆内存的初始值和最大值的平均数计算分区的尺寸,平均的堆尺寸会分出约 2000 个 Region。分区大小一旦设置,则启动之后不会再变化。如下图简单画了下 G1 分区模型。


![](https://img-blog.csdnimg.cn/20190222222954248.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,sha


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


dow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2l2YV9icm90aGVy,size_16,color_FFFFFF,t_70)


Eden regions(年轻代-Eden 区)


Survivor regions(年轻代-Survivor 区)


Old regions(老年代)


Humongous regions(巨型对象区域)


Free regions(未分配区域,也会叫做可用分区)-上图中空白的区域


G1 中的巨型对象是指,占用了 Region 容量的 50%以上的一个对象。Humongous 区,就专门用来存储巨型对象。如果一个 H 区装不下一个巨型对象,则会通过连续的若干 H 分区来存储。因为巨型对象的转移会影响 GC 效率,所以并发标记阶段发现巨型对象不再存活时,会将其直接回收。ygc 也会在某些情况下对巨型对象进行回收。


分区可以有效利用内存空间,因为收集整体是使用“标记-整理”,Region 之间基于“复制”算法,GC 后会将存活对象复制到可用分区(未分配的分区),所以不会产生空间碎片。

[](

)3、G1 GC 的分类和过程


JDK10 之前的 G1 中的 GC 只有 Young?GC,Mixed?GC。Full?GC 处理会交给单线程的 Serial Old 垃圾收集器。

[](

)4、Young?GC 年轻代收集


在分配一般对象(非巨型对象)时,当所有 Eden region 使用达到最大阀值并且无法申请足够内存时,会触发一次 Young?GC。每次 Young?GC 会回收所有 Eden 以及 Survivor 区,并且将存活对象复制到 Old 区以及另一部分的 Survivor 区。到 Old 区的标准就是在 PLAB 中得到的计算结果。因为 Young?GC 会进行根扫描,所以会 stop the world。


Young?GC 的回收过程如下:


1、根扫描,跟 CMS 类似,Stop the world,扫描 GC Roots 对象。


2、处理 Dirty card,更新 RSet.


3、扫描 RSet,扫描 RSet 中所有 old 区对扫描到的 young 区或者 survivor 去的引用。


4、拷贝扫描出的存活的对象到 survivor2/old 区


5、处理引用队列,软引用,弱引用,虚引用


5、Mix?GC 混合收集


Mixed?GC 是 G1 GC 特有的,跟 Full GC 不同的是 Mixed GC 只回收部分老年代的 Region。哪些 old region 能够放到 CSet 里面,有很多参数可以控制。比如 G1HeapWastePercent 参数,在一次 young?GC 之后,可以允许的堆垃圾百占比,超过这个值就会触发 mixed?GC。


G1MixedGCLiveThresholdPercent 参数控制的,old 代分区中的存活对象比,达到阀值时,这个 old 分区会被放入 CSet。


Mixed?GC 一般会发生在一次 Young?GC 后面,为了提高效率,Mixed?GC 会复用 Young?GC 的全局的根扫描结果,因为这个 Stop the world 过程是必须的,整体上来说缩短了暂停时间。


Mix?GC 的回收过程可以理解为 Young?GC 后附加的全局 concurrent marking,全局的并发标记主要用来处理 old 区(包含 H 区)的存活对象标记,过程如下:


1. 初始标记(Initial Mark)。标记 GC Roots,会 STW,一般会复用 Young?GC 的暂停时间。如前文所述,初始标记会设置好所有分区的 NTAMS 值。


2. 根分区扫描(Root?Region?Scan)。这个阶段 GC 的线程可以和应用线程并发运行。其主要扫描初始标记以及之前 Young?GC 对象转移到的 Survivor 分区,并标记 Survivor 区中引用的对象。所以此阶段的 Survivor 分区也叫根分区(Root?Region)。


3. 并发标记(Concurrent?Mark)。会并发标记所有非完全空闲的分区的存活对象,也即使用了 SATB 算法,标记各个分区。


4. 最终标记(Remark)。主要处理 SATB 缓冲区,以及并发标记阶段未标记到的漏网之鱼(存活对象),会 STW,可以参考上文的 SATB 处理。


5. 清除阶段(Clean UP)。上述 SATB 也提到了,会进行 bitmap 的 swap,以及 PTAMS,NTAMS 互换。整理堆分区,调整相应的 RSet(比如如果其中记录的 Card 中的对象都被回收,则这个卡片的也会从 RSet 中移除),如果识别到了完全空的分区,则会清理这个分区的 RSet。这个过程会 STW。


清除阶段之后,还会对存活对象进行转移(复制算法),转移到其他可用分区,所以当前的分区就变成了新的可用分区。复制转移主要是为了解决分区内的碎片问题。

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
说一下 jvm 有哪些垃圾回收器?(1),java异常面试题