「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。
垃圾收集器
1.Serial / Serial Old 收集器
JVM最早的垃圾收集器,串行化执行模式,它是一个单线程的收集器,适合内从只有几十到一两百兆的堆空间进行回收。因为它是单线程进行垃圾回收所以用户线程停顿十分明显,目前JDK默认已经没有使用此收集器,但是可以使用-XX+UseSerialGCz指定开启串行收集器。Serial使用复制回收算法进行新生代回收,Serial Old使用标记-整理算法进行老年代回收工作。
Parallel Scavenge / Parallel Old 收集器
从JDK1.3开始,JVM就采用了多线程的垃圾回收机制,Parallel收集器更关注系统的吞吐量,能有效的利用CPU时间尽快完成垃圾回收任务,此收集器使用户几百到几千兆的堆内存空间进行回收;可以通过-XX+UseParallelGC开启此收集器,JDK1.8默认使用此垃圾收集器。Parallel Scavenge使用复制回收算法进行新生代回收,Parallel Old使用标记-整理算法进行老年代回收工作。
ParNew 收集器
实质上是Serial收集器的多线程版本,它只负责新生代的垃圾回收,根据上文图中的垃圾收集器分配图来看,它一般配合Serial和CMS配合使用。在JDK9以后将其合并到CMS中。
CMS 收集器
CMS收集器是一款追求最短回收停顿时间为目标的收集器。比较特殊的是,CMS收集器是回收老年代,而且是基于标记-清除算法实现,他的整个工作过程分为4步:
- 初始标记:此过程很短暂,仅仅只是标记一下GC Roots直连的对象。
- 并发标记:和用户线程同时进行,此过程进行GC Roots引用链的扫描,此过程相对于第一阶段比较漫长。
- 重新标记:因为并发标记阶段和用户线程共同执行,导致标记产生变动的一部分对象,相对于第二阶段此阶段过程比较快。
- 并发清除:和用户线程同时执行,清除标记的对象,完成垃圾回收工作。
我们可以使用-XX:UseConcmarkSweepGC开启CMS老年代和ParNew新生代的回收器。 CMS收集器是一个具有划时代意义的垃圾收集器,但是它也有缺点:
- CPU资源敏感:采用并发手机机制,当处理器核心数比较少时,CMS工作时会对用户体验造成影响。
- 浮动垃圾:因为CMS在并发清理阶段用户线程仍然一起执行着,伴随着程序运行当然会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法处理。所以CMS收集器会预留一部分内存用来处理这一部分数据,在JDK1.6版本中老年代空间使用率的阈值是92%,如果预留的内存不够用的时候会出现Concurrent Mode Failure,导致新的Full GC产生,这是JVM会临时启用Serial Old来替代CMS。
- 内存碎片:前文已经提到,CMS是唯一一个采用
标记-清除算法进行回收的收集器,所以会产生内存碎片问题。 之所以CMS采用标记-清除算法是因为如果采用标记-整理算法会涉及到对象内存地址的移动,此时需要暂停用户线程来修改栈中的地址,这和CMS设计的初衷不符。
G1 收集器
有一个划时代意义的收集器,因为传统的垃圾收集器仍然对线程暂停的时间不可预测,为了实现线程暂停的时间变为可预测,G1收集器将堆内存做了较大改变,将其划分为大小相等且独立的区域Region,每一个Region都可以根据需要扮演Eden、Survivor、old。Region中有一类特殊的Humongous区域,专门用来存储大对象,只要对象大小超过一个Region容量的一般即判定为大对象,如果对象更大将会放在多个连续的Humongous中,Humongous默认当做老年代来看待。我们可以通过-XX:+UseG1GC来开启使用G1收集器。G1新生代采用复制算法,老年代使用标记-整理算法
G1的过程分为以下几步:
- 初始标记:仅只是标记一下 GC Roots 能直接关联到的对象, 并且修改 TAMS 指针的值, 让下一阶段用户线程并发运行时, 能正确地在可用的 Region 中分配新对象。这个阶段需要停顿线程, 但耗时很短, 而且是借用进行 Minor GC 的时候同步完成的, 所以 G1 收集器在这个阶段实际并没有额外的停顿
TAMS:要达到 GC 与用户线程并发运行, 必须要解决回收过程中新对象的分配, 所以 G1 为每一个 Region 区域设计了两个名为 TAMS(Top at Mark Start) 的指针, 从 Region 区域划出一部分空间用于记录并发回收过程中的新对象。 这样的对象认为它们是存活的, 不纳入垃圾回收范围。 - 并发标记:从 GC Root 开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。 当对象图扫描 完 成以后并发时有引用变动的对象,这些对象会漏标漏 标的对象会被一个叫做SATB(snapshot-at-the-beginning)算法来解决,我们以后来探讨一下这个算法。
- 最终标记:对用户线程做另一个短暂的暂停, 用于处理并发阶段结后仍遗留下来的最后那少量的漏标对象。
- 筛选回收:负责更新 Region 的统计数据, 对各个 Region 的回收价值和成本进行排序, 根据用户所期望的停顿时间来制定回收计划, 可以自由选择任意多个 Region 构成回收集, 然后把决定回收的那一部分 Region 的存活对象复制到空的 Region 中, 再清理掉整个旧 Region 的全部空间。 这里的操作涉及存活对象的移动,是必须暂停用户线程, 由多条收集器线程并行完成的。
G1收集器的特点:
- 充分利用CPU资源,缩短用户线程停顿时间。
- 分代收集
- 不存在内存碎片,空间整齐