MENU

JVM 垃圾收集器

201919 7 种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明可以搭配使用。

Serial 收集器

201920

  • JVM 虚拟机运行在 Client 模式下的默认新生代收集器。
  • 是一个单线程收集器,单线程即说明它只会使用一个 CPU 或者一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。
  • 简单而高效 (与其他收集器的单线程比),对于限定单个 CPU 的环境来说,Serial 收集器由于没有线程交互的开销,因此获得最高的单线程收集效率
  • 在 Client 场景下,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代停顿时间完全可以控制在几十毫秒最多一百多毫秒以内。

ParNew 收集器

201921

  • 是 Serial 收集器的多线程版本
  • 运行在 Server 模式下的虚拟机首选的新生代收集器
  • 除 Serial 收集器外,目前只有它能与 CMS 收集器配合工作
  • 由于存在线程交互的开销,ParNew 收集器在单 CPU 的环境中绝对不会有比 Serial 收集器更好的效果。随着 CPU 数量增加,默认开启的收集器线程数与 CPU 的数量相同,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。

Parallel Scavenge 收集器

201922

  • 多线程新生代收集器
  • 基于复制算法实现
  • 其它收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而 Parallel Scavenge 收集器的目标则是达到一个可控制的吞吐量。吞吐量是指:CPU 用于运行代码的时间与 CPU 总消耗时间的比值,即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)

Serial Old 收集器

201920

  • 是 Serial 收集器的老年代版本,是一个单线程收集器
  • 采用 “标记 - 整理” 算法
  • Client 模式下的虚拟机使用
  • 在 Server 模式下,则它主要还有两大用途:一种用途是在 JDK1.5 以及之前版本中与 Parallel Scavenge 收集器搭配使用;另外一种用途是作为 CMS 收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

Parallel Old 收集器

201922

  • 是 Parallel Scavenge 收集器的老年代版本,是一个多线程收集器
  • 采用 “标记 - 整理” 算法
  • 新生代 Parallel Scavenge 收集器由于无法与 CMS 收集器配合工作,只能与 Serial Old 收集器配合工作,但该收集器在服务端性能差,使用了 Parallel Scavenge 收集器也未必能在整体应用上获得吞吐量最大化的效果,由于单线程的老年代收集无法充分利用服务器多 CPU 的处理能力,在老年代很大而且硬件比较高级的环境还不一定有ParNew+CMS的组合
  • Parallel Old 收集器的出现,在注重吞吐量以及 CPU 资源敏感的场合,优先考虑Parallel Scacenge + Parallel Old收集器。

CMS 收集器

201923

  • 一种以获取最短回收停顿时间为目标的收集器
  • 采用 “标记 - 清除” 算法
  • 收集过程分为 4 步:
    • 初始标记 (CMS initial mark):仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿
    • 并发标记 (CMS concurrent mark):进行 RootsTracing 过程,不需要停顿,耗时长
    • 重新标记 (CMS remark):为了修正并发标记期间因用户重新继续运作而导致标记产生的变动的哪一部分对象的标记记录,需要停顿
    • 并发清除 (CMS concurrent sweep):不需要停顿

    由于整个过程中耗时最长的并发标记和并发清除过程收集器线程可以与用户线程一起工作,不需要停顿。

  • 存在的缺点:
    • CMS 收集器对 CPU 资源非常敏感,它虽然不会导致用户线程停顿,但会因为占用一部分线程 (CPU 资源) 而导致应用程序变慢,总吞吐量降低
    • 无法处理浮动垃圾,可能出现Concurrent Mode Failure失败而导致另一次 Full GC 的产生。浮动垃圾是指由于 CMS 并发清理阶段用户线程还在运行着,伴随着程序运行自然会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS 无法在打次收集中处理掉他们,只好留待下一次 GC 时再清理掉。CMS 运行期间预留的内存无法满足程序需要,就会出现一次Concurrent Mode Failure失败,则将启动后备预案:临时请用 Serial Old 收集器重新进行老年代的垃圾收集,造成停顿时间加长,性能下降。
    • 基于 “标记 - 清除” 算法,会有大量的空间碎片产生,往往会出现老年代还有很大空间剩余,但无法找到足够大的连续空间来分配当前对象,不得不提前触发一次 Full GC。

G1 收集器

201924

  • 面向服务端应用的垃圾收集器
  • 具备特点:
    • 并行与并发:G1 能充分利用多 CPU、多核环境下的硬件优势,使用多个 CPU(CPU 或者 CPU 核心) 来缩短停顿时间,部分收集器原需要停顿执行 GC 动作,G1 收集器可以通过并发的方式让 Java 程序继续执行。
    • 分代收集:与其它收集器一样,分代概念在 G1 中依然得到保留。可以不需要其它收集器配合就能独立管理整个 GC 堆。
    • 空间整合:从整体来看是基于”标记 - 整理 “算法实现的收集器,从局部(两个 Region 之间) 上来看是基于”复制“算法实现,但无论如何这两种算法都意味着 G1 运作期间不会产生内存空间碎片。
    • 可预测的停顿:降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间判断内,消耗在垃圾收集上的时间不得超过 N 毫秒。
  • G1 收集器,它将整个 Java 堆划分为多个大小相等的独立区域 (Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不在物理隔离,它们都是一部分 Region 的结合。
  • G1 收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个 Java 堆中进行全区域的垃圾收集。G1 跟踪各个 Region 里面的垃圾堆积的价值大小 (回收所获得的空间大小以及回收所需要的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region。
  • 每个 Region 都有一个与之对应的 Remembered Set,用来检查 Reference 引用的对象是否处于不同的 Region 之中,从而保证不对全堆扫描也不会有遗漏。
  • 不维护 Remembered Set,将分为下面 4 个步骤
    • 初始标记 (Initial Marking):仅仅只是标记一下 GC Roots 能直接关联到的对象,并修改 TEMS(Next Top at Mark Start) 的值,让下一阶段用户程序并发允许时,能够正确可用 Region 中创建新对象,该阶段需要停顿,但耗时很短
    • 并发标记 (Concurrent Marking):从 GC Root 开始对堆中对象进行可达性分析,找出存活的对象,该阶段比较耗时,但可与用户程序并发执行
    • 最终标记 (Final Marking):为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这端时间对象变化记录在线程 Reembered Set Logs 里面,并把 Remembered Set Logs 的数据合并到 Remembered Set 中,该阶段需要停顿线程,但是可并行执行
    • 筛选回收 (Live Data Counting Evacuation):首先对各个 Region 的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。
标签: Java, JVM
返回文章列表 文章二维码 打赏
本页链接的二维码
打赏二维码