最全的 JVM 面试知识点(三):垃圾收集器

在涉及 Java 相关的面试中,面试官常常会让讲讲 Java 中的垃圾收集相关的理解和常见的分类。可见,光就应付面试而言,JVM 的垃圾收集也对每一位 Java 开发者很重要。除此以外,对于咱们了解和解决 Java 应用的性能时,也颇有帮助。面试

上一篇介绍了 Java 虚拟机内存的垃圾收集算法。本章将会介绍 Java 中经常使用的垃圾收集器及其特性。算法

本文的主要内容:微信

  • 基本概念
    • 串行、并行和并发
    • JVM 垃圾收集中的串行、并行和并发
  • 串行垃圾回收器
    • Serial
    • Serial Old
  • 并行垃圾回收器
    • ParNew
    • Parallel
    • Parallel Old
    • CMS
    • G1(Garbage First)
  • 小结

基本概念

在介绍具体的垃圾回收器以前,咱们先了解几个基本概念。多线程

串行、并行和并发

计算机系统的信息交换有两种方式:并行数据传输方式和串行数据传输方式。闭包

  • 串行:计算机中的串行是用 Serial 表示。A 和 B 两个任务运行在一个 CPU 线程上,在 A 任务执行完以前不能够执行 B。即,在整个程序的运行过程当中,仅存在一个运行上下文,即一个调用栈一个堆。程序会按顺序执行每一个指令。
  • 并行:并行性指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不一样 CPU 上同时执行。好比,A 和 B 两个任务能够同时运行在不一样的 CPU 线程上,效率较高,但受限于 CPU 线程数,若是任务数量超过了 CPU 线程数,那么每一个线程上的任务仍然是顺序执行的。
  • 并发:并发指多个线程在宏观(相对于较长的时间区间而言)上表现为同时执行,而其实是轮流穿插着执行,并发的实质是一个物理 CPU 在若干道程序之间多路复用,其目的是提升有限物理资源的运行效率。 并发与并行串行并非互斥的概念,若是是在一个CPU线程上启用并发,那么天然就仍是串行的,而若是在多个线程上启用并发,那么程序的执行就能够是既并发又并行的。

举个简单的例子,写代码和听音乐这两件事:并发

  • 咱们在编码的时候为了保证注意力集中,其余什么也不干。在茶歇的时候才听音乐。
  • 咱们在编码的时候,既能够写代码,也能够听音乐,这两件事同时进行。
  • 咱们会常常写一段时间代码就歇一下听音乐,这两件事穿插着进行。

读者能够思考上面所述的串行、并行和并发。jvm

JVM 垃圾收集中的串行、并行和并发

在 JVM 垃圾收集器中也涉及到如上的三个概念。分布式

  • 串行(Serial):使用单线程进行垃圾回收的回收器。
  • 并行(Parallel):指多条垃圾收集线程并行工做,但此时用户线程仍然处于等待状态。
  • 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不必定是并行,可能会交替执行),用户程序在继续运行,而垃圾收集器运行在另外一个 CPU 上。

在了解了这些概念以后,咱们开始具体介绍经常使用的垃圾收集器。性能

串行垃圾回收器

如上所述,串行回收器是指使用单线程进行垃圾回收的回收器,每次回收时串行回收器只有一个工做线程,对于并发能力较弱的计算机来讲,串行回收器的专一性和独占性每每有更好的表现。串行回收器能够在新生代和老年代使用,根据做用的堆空间不一样,分为新生代串行回收器和老年代串行回收器。优化

Serial

Serial收集器是最古老的收集器,它的缺点是当Serial收集器想进行垃圾回收的时候,必须暂停用户的全部进程,即 STW(服务暂停)。到如今为止,它依然是虚拟机运行在 client 模式下的默认新生代收集器。

参数控制:-XX:+UseSerialGC 使用串行收集器。

Serial Old

Serial 收集器的老年代版本,它一样是一个单线程收集器。它主要有两大用途:一种用途是在 JDK1.5 以及之前的版本中与 Parallel Scavenge 收集器搭配使用,另外一种用途是做为 CMS 收集器的后备方案。

UseSerialGC:开启此参数使用 Serial & Serial Old 搜集器(client 模式默认值)。

并行垃圾回收器

并行回收器是在串行回收器的基础上作了改进,它可使用多个线程同时进行垃圾回收,对于计算能力强的计算机来讲,能够有效的缩短垃圾回收所需的实际时间。

ParNew

ParNew 收集器是一个工做在新生代的垃圾收集器,它只是简单的将串行收集器多线程化,它的回收策略和算法和串行回收器同样。新生代并行,老年代串行;新生代复制算法、老年代标记-整理。

参数控制:-XX:+UseParNewGC 使用 ParNew 收集器;-XX:ParallelGCThreads 限制线程数量 除了 Serial 收集器外,只有它能与 CMS 收集器(真正意义上的并发收集器,后面会介绍到)配合工做。

Parallel

Parallel 是采用复制算法的多线程新生代垃圾回收器,Parallel 收集器更关注系统的吞吐量。所谓吞吐量就是 CPU 用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间/(运行用户代码时间 + 垃圾收集时间)。

停顿时间越短就越适合须要与用户交互的程序,良好的响应速度可以提高用户的体验;

而高吞吐量则能够最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台运算而不须要太多交互的任务。

能够经过参数来打开自适应调节策略,虚拟机会根据当前系统的运行状况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;也能够经过参数控制GC的时间不大于多少毫秒或者比例;新生代复制算法、老年代标记-整理

参数控制:

  • -XX:MaxGCPauseMillis 设置最大垃圾收集停顿时间
  • -XX:GCTimeRatio 设置吞吐量的大小(默认是99)
  • -XX:+UseAdaptiveSeizPolicy 打开自适应模式,当这个参数打开以后,就不须要手工指定新生代的大小、Eden与Survivor区的比例、晋升老年代对象年龄等细节参数了,虚拟机会根据当前系统的运行状况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量

Parallel Old

Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本,采用多线程和标记-整理算法,也是比较关注吞吐量。在注重吞吐量及 CPU 资源敏感的场合,均可以优先考虑 Parallel 加 Parallel Old 收集器。

参数控制:-XX:+UseParallelOldGC 使用 Parallel Old 收集器;-XX:ParallelGCThreads 限制线程数量。

CMS 垃圾回收器

CMS(Concurrent Mark Sweep) 并发标记请除,它使用的是标记-清除法,工做在老年代,主要关注系统的停顿时间。

CMS 并非独占的回收器,也就是说,CMS 回收的过程当中应用程序仍然在不停的工做,又会有新的垃圾不断的产生,因此在使用CMS的过程当中应该确保应用程序的内存足够可用,CMS不会等到应用程序饱和的时候才去回收垃圾,而是在某一阀值(默认为68)的时候开始回收,也就是说当老年代的空间使用率达到68%的时候会执行CMS。若是内存使用率增加很快,在CMS执行过程当中,已经出现了内存不足的状况,此时,CMS回收就会失败,虚拟机将启动老年代 Serial 进行垃圾回收,这会致使应用程序中断,直到垃圾回收完成后才会正常工做,这个过程GC的停顿时间可能较长,因此阀值的设置要根据实际状况设置。

  • 初始标记:暂停全部的其余线程,并记录下直接与root相连的对象,速度很快;
  • 并发标记:同时开启GC和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前全部的可达对象。由于用户线程可能会不断的更新引用域,GC线程没法保证可达性分析的实时性。因此这个算法里会跟踪记录这些发生引用更新的地方。
  • 从新标记:从新标记阶段就是为了修正并发标记期间由于用户程序继续运行而致使标记产生变更的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段的时间稍长,远远比并发标记阶段时间短
  • 并发清除:开启用户线程,同时GC线程开始对未标记的区域作清扫。

主要优势:并发收集、低停顿。可是它有下面三个明显的缺点:

  • 对CPU资源敏感;
  • 没法处理浮动垃圾;
  • 使用的 标记-清除 算法会致使收集结束时会有大量空间碎片产生。

标记清除法产生的内存碎片问题,CMS 提供提供了一些优化设置,能够设置完成 CMS 以后进行一次碎片整理,也能够设置进行多少次 CMS 回收后进行碎片整理。

参数控制:

  • -XX:+UserConcMarkSweepGC 使用 CMS 垃圾清理器
  • -XX:CMSInitatingPermOccupancyFraction 设置阀值
  • -XX:ConcGCThreads 限制线程数量
  • -XX:+UseCMSCompactAtFullCollection 设置完成 CMS 以后进行一次碎片整理
  • -XX:CMSFullGCsBeforeCompaction 设置进行多少次 CMS 回收后进行碎片整理

G1(Garbage First)

G1(Garbage First) 垃圾收集器是当今垃圾回收技术最前沿的成果之一。早在 JDK7 就已加入 JVM 的收集器你们庭中,成为 HotSpot 重点发展的垃圾回收技术。

G1 收集器在后台维护了一个优先列表,每次根据容许的收集时间,优先选择回收价值最大的 Region。包括:Eden、Survivor、Old 和 Humongous。

其中,Humongous 是特殊的 Old 类型,回收空闲巨型分区,专门放置大型对象。这样的划分方式意味着不须要一个连续的内存空间管理对象。G1 将空间分为多个区域,优先回收垃圾最多的区域。一个对象和它内部所引用的对象可能不在同一个 Region 中,那么当垃圾回收时,是否须要扫描整个堆内存才能完整地进行一次可达性分析?

固然不是,每一个 Region 都有一个 Remembered Set(已记忆集合),用于记录本区域中全部对象引用的对象所在的区域,从而在进行可达性分析时,只要在 GC Roots 中再加上 Remembered Set 便可防止对全部堆内存的遍历。

同 CMS 垃圾回收器同样,G1 也是关注最小时延的垃圾回收器,也一样适合大尺寸堆内存的垃圾收集,官方也推荐使用 G1 来代替选择 CMS。G1 最大的特色是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其余收集器甚至 CMS 的众多缺陷。

G1收集器的运做大体分为如下几个步骤:

  • 初始标记:初始标记阶段仅仅只是标记一下 GC Roots 能直接关联到的对象,而且修改 TAMS 的值,让下一个阶段用户程序并发运行时,能在正确可用的 Region 中建立新对象,这一阶段须要停顿线程,可是耗时很短。
  • 并发标记:并发标记阶段是从 GC Root 开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行。
  • 最终标记:而最终标记阶段则是为了修正在并发标记期间因用户程序继续运做而致使标记产生变更的那一部分标记记录,虚拟机将这段时间对象变化记录在线程 Remenbered Set Logs 里面,最终标记阶段须要把Remembered Set Logs 的数据合并到 Remembered Set Logs 里面,最终标记阶段须要把 Remembered Set Logs 的数据合并到 Remembered Set 中,这一阶段须要停顿线程,可是可并行执行。
  • 筛选回收:最后在筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所指望的 GC 停顿时间来制定回收计划。

G1 能充分利用多 CPU、多核环境下的硬件优点,使用多 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿的时间,部分其余收集器本来须要停顿 Java 线程执行的GC动做,G1 收集器仍然能够经过并发的方式让 Java 程序继续执行。

此外,与其余收集器同样,分代概念在G1中依然得以保留。虽然 G1 能够不需其余收集器配合就能独立管理整个 GC 堆,但它可以采用不一样的方式去处理新建立的对象和已经存活了一段时间、熬过屡次 GC 的旧对象以获取更好的收集效果。

空间整合:与 CMS 的 标记-清理 算法不一样,G1 从总体看来是基于 标记-整理 算法实现的收集器,从局部(两个 Region 之间)上看是基于 复制 算法实现,不管如何,这两种算法都意味着 G1 运做期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会由于没法找到连续内存空间而提早触发下一次 GC。

可预测的停顿:这是 G1 相对于 CMS 的另一大优点,下降停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还能创建可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片断内,消耗在垃圾收集上的时间不得超过 N 毫秒,这几乎已是实时 Java(RTSJ)的垃圾收集器特征了。

参数控制:-XX:+UseG1GC

小结

本文介绍了常见的7种不一样分代的收集器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;而它们所处区域,则代表其是属于新生代收集器仍是老年代收集器:

  • 新生代收集器:Serial、ParNew、Parallel Scavenge;
  • 老年代收集器:Serial Old、Parallel Old、CMS;
  • 整堆收集器:G1;

根据收集的区域(年轻代或年老代)和收集器自身的特性,能够有以下组合: Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel/Serial Old、Parallel/Parallel Old、G1。

ZGC 来了。ZGC 是 JDK11 中要发布的最新垃圾收集器。彻底没有分代的概念,官方给出 ZGC 的优势是无碎片,时间可控,超大堆。读者能够尝试了解和使用一下 ZGC 。

推荐阅读

最全的 JVM 面试知识点系列文章

订阅最新文章,欢迎关注个人公众号

微信公众号

参考

  1. 从串行到并行,从并行到分布式
  2. 详解 JVM Garbage First(G1) 垃圾收集器
相关文章
相关标签/搜索