深入理解 Java 虚拟机:JVM 中的 GC 垃圾收集器总结

前言

垃圾收集器是对应垃圾收集算法的具体实现,并且不同的垃圾收集器对应不同应用场景,会有不同的组合。
因此并不存在完美的垃圾收集器,需要通过了解垃圾收集器的特性与组合,才能在调优时选择最适合的收集器。

新生代收集器

Serial 收集器

Serial 收集器最基本、发展历史最悠久的收集器。(老古董?)

特点: 单线程的收集器

垃圾收集算法: 复制算法

优点: 简单而高效,对于限定单个 CPU 环境来说,由于没有线程交互的开销,专心做垃圾收集从而获得最高的单线程收集效率。

缺点: 它进行垃圾收集时,必须暂停其他所有的工作线程(即 Stop The World),直到它收集结束。(用户体验很差)

应用场景: Serial 收集器对于运行在 Client 模式下的虚拟机来说是一个很好的选择。

组合: Serial 收集器(新生代) + Serial Old 收集器(老年代)
在这里插入图片描述

ParNew 收集器

ParNew 收集器Serial 收集器多线程版本,因此所有可用的参数,收集算法,Stop The World,对象分片规则,回收策略都与 Serial 收集器一样

特点: 多线程收集器

垃圾收集算法: 复制算法

缺点: 它进行垃圾收集时,必须暂停其他所有的工作线程(即 Stop The World),直到它收集结束。(用户体验很差)

线程数: 默认开启的收集线程数与 CPU 数相同

VM 指令:

  • -XX:+UseConcMarkSweepGC: 开启这个参数后,默认使用 ParNew 收集器
  • -XX:+UseParNewGC: 强制使用 ParNew 收集器
  • -XX:ParallelGCThreads: 指定收集线程数量

组合:

  1. ParNew 收集器(新生代) + Serial Old 收集器(老年代)
  2. ParNew 收集器(新生代) + CMS 收集器(老年代)
    在这里插入图片描述

Parallel Scavenge 收集器

Parallel Scavenge 是一个新生代收集器,又叫吞吐量优先收集器

特点: 并行的多线程收集器,这个收集器的目标准则是达到一个可控制的吞吐量
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验;
而高吞吐量能高效的利用 CPU 时间,尽快的完成程序的运行任务,主要适合在后台运算而不需要太多交互的任务。

吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)
PS:虚拟机运行 100 分钟,其中垃圾回收用时 1 分钟,吞吐量就是 99%

垃圾收集算法: 复制算法

VM 指令:

  1. -XX:MaxGCPauseMillis: 控制最大垃圾收集停顿时间,单位毫秒
  2. -XX:GCTimeRatio: 直接设置吞吐量大小,该值 0 < x < 100 的整数
    即最大 GC 停顿时间为 1 / (1 + x),默认值为 99
  3. -XX:+UseAdaptiveSizePolicy: 当这个参数开启后,即开启了 GC 自适应策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调节参数以适应停顿的时间或最大吞吐量

应用场景: 注重高吞吐量,CPU 资源敏感的系统,比如大数据统计计算系统

组合:

  1. Parallel Scavenge 收集器(新生代) + Serial Old 收集器(老年代) JDK 1.6 之前,别无选择
  2. Parallel Scavenge 收集器(新生代) + Parallel Old 收集器(老年代) JDK 1.6 开始

在这里插入图片描述

老年代收集器

Serial Old 收集器

Serial Old 收集器Serial 收集器的老年代版本,JDK 1.6 开始提供

特点: 单线程的收集器

垃圾收集算法: 标记-整理算法

优点: 简单而高效,对于限定单个 CPU 环境来说,由于没有线程交互的开销,专心做垃圾收集从而获得最高的单线程收集效率。(同 Serial 收集器)

缺点: 它进行垃圾收集时,必须暂停其他所有的工作线程(即 Stop The World),直到它收集结束。(同 Serial 收集器)

应用场景:

  1. 运行在 Client 模式下的虚拟机。(同 Serial 收集器)
  2. Server 模式下CMS 收集器 的后备方案,在并发发生 Concurrent Mode Failure 时使用。
  3. Server 模式下,JDK 1.5 及以前版本中与 Parallel Scavenge 收集器搭配使用。(也就是说现在不用了)

组合: Serial 收集器(新生代) + Serial Old 收集器(老年代)
在这里插入图片描述

Parallel Old 收集器

Parallel Old 收集器Parallel Scavenge 收集器的老年代版本

特点: 多线程收集器

垃圾收集算法: 标记-整理算法

应用场景: 注重高吞吐量,CPU 资源敏感的系统,比如大数据统计计算系统

组合: Parallel Scavenge 收集器(新生代) + Parallel Old 收集器(老年代)
在这里插入图片描述

CMS 收集器

CMS 收集器是一种获取最短回收停顿时间为目标的收集器。

特点: 重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验

垃圾收集算法: 标记-清除算法

步骤:

  1. 初始标记: 标记 GC Root 能直接关联的对象,速度很快
  2. 并发标记: 进行 GC Root Tracing 过程,与并发清除一同工作,耗时长
  3. 重新标记: 修正并发标记期间因用户程序运行导致的标记产生变动的那一部分对象的标记记录
  4. 并发清除: 垃圾回收,与并发标记一同工作,耗时长

优点: 并发收集,低停顿。整个回收中,耗时最长的 并发标记并发清除 都可以与用户线程并发运行

缺点:

  1. 并发阶段,虽然不会停顿用户线程,但是会占用一部分 CPU 资源,导致应用程序变慢,总吞吐量降低。默认线程数是 (CPU 数量 + 3)/ 4,当 4 个 CPU 以上时,并发回收垃圾收集线程占用资源不小于 25%,随 CPU 数量增加而下降。
  2. CMS 收集器无法处理浮动的垃圾 (标记阶段后又产生的垃圾,只能放下次 GC 回收),可能出现 Concurrent Mode Failure 导致 Full GC
  3. 由于是并发回收,因此老年代不能等几乎满了再去回收,需要 预留一部分空间提供并发收集时的程序运作使用
  4. Concurrent Mode Failure 失败就会导致启用后备方案 Serial Old 收集器 进行老年代垃圾回收,停顿更久。
  5. 基于 标记-清除算法 实现的回收,因此 存在大量空间碎片的产生,会给大对象分配带来大麻烦

VM 指令:

  1. -XX:CMSInitiatingOccupancyFraction: 老年代使用阈值,达到后启用 GC,设置太高可能导致 Concurrent Mode Failure,JDK 1.5 默认值为 68%,JDK 1.6 默认值为 92%。
  2. -XX:+UseCMSCompactAtFullCollection: 开关参数,默认开启,用于在 CMS 收集器 顶不住要进行 Full GC 时,开启内存碎片的合并整理过程。
  3. -XX:CMSFullGCsBeforeCompaction: 设置执行多少次不压缩的 Full GC 后,跟着来一次带压缩的。(默认值为 0,表示每次进行 Full GC 都进行碎片整理)

应用场景: Java 应用集中在互联网站或 B/S 系统的服务端上

组合: ParNew 收集器(新生代) + CMS 收集器(老年代)
在这里插入图片描述

两用收集器

G1 收集器

G1 收集器是一款 面向服务端应用的垃圾收集器 ,JDK 1.7 中被视为最前沿的成果之一,JDK 1.9 默认垃圾收集器为 G1

特点:

  1. 并行与并发: 充分利用 CPU 资源、多核环境下的硬件优势,使用多 CPU 来缩短 Stop-The-World 的时间,且 G1 收集器 依然能通过并发的方式让 Java 代码继续运行。
  2. 分代收集: 依然保留 分代收集 的思想,且无需与其他收集器配合,能够 独立管理整个 GC 堆
  3. 空间整合: 基于 标记-整理 算法实现,不会产生空间内存碎片
  4. 可预测与停顿: 能建立 可预测的停顿时间模型,使用者能明确指定在一个长度为 M 毫秒的时间内,消耗在垃圾回收上的时间不超过 N 毫秒

Region 区域:
G1 将内存分为多个大小相等的独立区域 Region,虽然保留新生代,老年代的概念,但是已经不是物理上的隔离,他们都是一部分 Region(无需连续) 的集合。
G1 跟踪各个 Region 里面的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region

垃圾收集算法: 标记-整理算法

步骤:

  1. 初始标记: 标记 GC Roots 能直接管理到的对象,并且修改 TAMS 的值,让下一阶段用户程序并发执行时,能在正确可用的 Region 中创建对象,需要停顿线程,耗时短。
  2. 并发标记:GC Roots 开始对堆中的对象,进行可达性分析,找出存活东西,这阶段耗时较长,可与用户程序并发执行。
  3. 最终标记: 修正在 并发标记 期间导致的产生变动的标记记录,这个时间需要停顿线程,但是可并行执行。
  4. 筛选回收: 根据 Region 的价值和成本进行排序,执行回收计划。

组合: G1 收集器 无需与其他收集器配合,能够 独立管理整个 GC 堆
在这里插入图片描述

各版本默认收集器

JDK1.7 默认垃圾收集器:Parallel Scavenge(新生代)+ Parallel Old(老年代)

JDK1.8 默认垃圾收集器:Parallel Scavenge(新生代)+ Parallel Old(老年代)

JDK1.9 默认垃圾收集器:G1

垃圾收集器选择

纯属个人理解:

注重吞吐量系统:尽可能高效的利用 CPU 资源

  1. JDK 1.6 及以前: ParNew 收集器(新生代) + CMS 收集器(老年代)
  2. JDK 1.6 及以后: Parallel Scavenge 收集器(新生代) + Parallel Old 收集器(老年代)

一般 Java B/S 系统:注重快速响应,提升用户体验

  1. JDK 1.8 及以前: ParNew 收集器(新生代) + CMS 收集器(老年代)
  2. JDK 1.9 开始: G1 收集器

常用JVM 参数

垃圾回收统计信息

  1. -XX:+PrintGC: 打印 GC
  2. -XX:+PrintGCDetails: 打印GC详细信息
  3. -XX:+PrintGCTimeStamps: 打印CG发生的时间戳
  4. -Xloggc:filename: 指定 GC 日志的位置
  5. -verbose:gc: 输出虚拟机中GC的详细情况
  6. -XX:+PrintCommandLineFlagsjvm: 参数可查看默认设置收集器类型

堆设置

  1. -Xms: 初始堆大小
  2. -Xmx: 最大堆大小
  3. -Xmn: 新生代大小
  4. -XX:NewRatio: 设置新生代和老年代的比值。如:为3,表示年轻代与老年代比值为1:3
  5. -XX:SurvivorRatio: 新生代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:为3,表示Eden: Survivor=3:2,一个Survivor区占整个新生代的1/5
  6. -XX:MaxTenuringThreshold: 设置转入老年代的存活次数。如果是0,则直接跳过新生代进入老年代
  7. -XX:PermSize、-XX:MaxPermSize: 分别设置永久代最小大小与最大大小(Java8以前)
  8. -XX:MetaspaceSize、-XX:MaxMetaspaceSize: 分别设置元空间最小大小与最大大小(Java8以后)

收集器设置

  1. -XX:+UseSerialGC: 设置串行收集器
  2. -XX:+UseParallelGC: 设置并行收集器
  3. -XX:+UseParalledlOldGC: 设置并行老年代收集器
  4. -XX:+UseConcMarkSweepGC: 设置并发收集器

并行收集器设置

  1. -XX:ParallelGCThreads=n: 设置并行收集器收集时使用的CPU数。并行收集线程数。
  2. -XX:MaxGCPauseMillis=n: 设置并行收集最大暂停时间
  3. -XX:GCTimeRatio=n: 设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) 并发收集器设置
  4. -XX:+CMSIncrementalMode: 设置为增量模式。适用于单CPU情况。
  5. -XX:ParallelGCThreads=n: 设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值