本篇内容包括:7 种 Jvm 垃圾回收器的介绍、对比 以及 对应的 Jvm 参数设置,这 7 种包括了:Serial、ParNew 以及 Parallel Scavenge 三种新生代回收器 和 :Serial Old、Parallel Old 以及 CMS 三种老年代回收器,此外还有一个 G1 回收器是 Java 目前比较前沿的成果…
一、Jvm 垃圾回收器概述
我们前面提到了,垃圾回收器的 回收的内容、回收的时机以及回收的方式,接下来我们来看 Java 垃圾回收器。如果垃圾回收算法是内存回收的方法论的话,那么垃圾回收器就是内存回收的具体实现了。
Jvm 的垃圾回收器根据场景和实现方式可以分为新生代回收器和老年代回收器,新生代回收器与老年代回收器可以搭配使用。
- 新生代回收器包括:Serial、ParNew 以及 Parallel Scavenge;
- 老年代回收器包括:Serial Old、Parallel Old 以及 CMS;
- 此外,Java7 update 4(第七版第四个更新升级包)之后引入了一个 G1 收集器。
Ps:不同垃圾回收器适合于不同的内存区域,有的两个垃圾回收器之间也可以配合使用!
二、新生代回收器
1、Serial 收集器
Serial 收集器是最基础且历史最悠久的垃圾收集器,作为单线程工作的收集器。Serial 会在它工作时要求暂停用户所有的其他线程(Stop-the-World 机制)。采用的是 “标记-复制” 算法。垃圾清理时,Serial 回收器不存在线程间的切换,因此,在单 CPU 的环境下,垃圾清除效率比较高。
虽然 Serial 收集器是最基础最老的收集器,但是迄今为止 HotSpot 虚拟机运行在 Client 模式下的默认的新生代垃圾收集器!相较于其他收集器,Serial 具有以下 3 个优点:
- 简单高效;
- 所有收集器中额外内存消耗最少;
- 针对内存几十兆或一两百兆的新生代,停顿时间能控制在一百毫秒内。
Ps:这里需要注意的一点是 Serial 收集器和 Serial Old 收集器在垃圾收集时不是单线程的,通常所描述的"单线程"是指的垃圾回收时暂停其他所有的工作线程。
2、ParNew 收集器
ParNew 收集器大致可以理解为是 Serial 收集器的多线程版本,因为 ParNew 收集器除了采用并行回收的方式执行内存回收外,与 Serial 收集器之间几乎没有任何区别。ParNew 收集器在年轻代中同样也是采用复制算法、Stop-the-World 机制。
ParNew 收集器是很多版本 Jvm(包括 HotSpot)运行在 Server 模式下新生代的默认垃圾收集器。
ParNew 收集器运行在多 CPU 的环境下,由于可以充分利用多 CPU、多核心等物理硬件资源优势,可以更快的完成垃圾收集,提升程序的吞吐量。但是在单个 CPU 的环境下,ParNew 收集器不比 Serial 收集器更高效,虽然 Serial 收集器是基于串行回收,但是由于 CPU不需要频繁的切换,因此可以有效避免多线程交互过程中产生的一些额外开销。
Ps:除了 Serial 外,目前只有 ParNew GC 能与 CMS 收集器配合工作。
3、Parallel Scavenge 收集器
Parallel Scavenge 收集器同 ParNew 收集器一样,也是采用 “标记-复制” 算法,且为能够并行收集的多线程收集器。
Parallel Scavenge 的特点是其关注重点为吞吐量,高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算,但也就是说它的线程单次停止时间可能更长,因此适用于后台计算型任务程序:
Ps:如果虚拟机完成某个任务,用户代码加上垃圾收集总共耗费了 100 分钟,其中垃圾收集花掉 1 分 钟,那吞吐量就是 99% :
三、老年代回收器
1、Serial Old 收集器
Serial Old 是 Serial 收集器的老年代版本,采用“标记-整理”算法,单线程收集器,也是给 Client 模式下的虚拟机使用。
Ps:在 Jdk1.5 及其以前,它常与 Parallel Scavenge 回收器配合使用,达到较好的吞吐量,另外它也是 CMS 回收器在 Concurrent Mode Failure 时的后备方案。
2、Parallel Old 收集器
Parallel Old 回收器是 Parallel Scavenge 回收器的老生代版本,属于多线程回收器,采用“标记-整理”算法。Parallel Old 回收器和 Parallel Scavenge 回收器同样考虑了吞吐量优先这一指标,非常适合那些注重吞吐量和 CPU 资源敏感的场合。
Ps:在注重吞吐量以及 CPU 资源敏感的场合,可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器
3、CMS 收集器
CMS 收集器,是一种以获取最短回收停顿时间为目标的收集器,其缩写含义为 Concurrent Mark Sweep,Mark Sweep 指的是“标记-清除”算法,在互联网网站、B/S 架构的中常用的收集器就是 CMS,因为系统停顿的时间最短,给用户带来较好的体验。
CMS收集器的运作过程分为4个步骤,包括:
- 初始标记(短暂),仅仅只是标记一下 GCRoots 能直接关联到的对象,速度很快;
- 并发标记(和用户的应用程序同时进行),进行 GCRoots 追踪的过程,标记从 GCRoots 开始关联的所有对象开始遍历整个可达分析路径的对象。这个时间比较长,所以采用并发处理(垃圾回收器线程和用户线程同时工作);
- 重新标记(短暂),为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短;
- 并发清除(和用户的应用程序同时进行):垃圾回收
整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,从总体上来说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。
四、G1 收集器
G1(Garbage-First)收集器是最前沿的成果之一,在Java7 update 4之后引入(Jdk7 的第 4 个版本),是一款面向服务端应用的垃圾收集器。在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。
G1 是一个分代的,增量的,并行与并发的“标记-复制”垃圾回收器。它的设计目标是为了适应现在不断扩大的内存和不断增加的处理器数量,进一步降低暂停时间(pause time),同时兼顾良好的吞吐量。
G1收集器的优势:
- 独特的分代垃圾回收器,分代GC: 分代收集器,同时兼顾年轻代和老年代;
- 使用分区算法,不要求 eden,年轻代或老年代的空间都连续;
- 并行性: 回收期间,可由多个线程同时工作,有效利用多核cpu资源;
- 空间整理: 回收过程中,会进行适当对象移动,减少空间碎片;
- 可预见性: G1 可选取部分区域进行回收,可以缩小回收范围,减少全局停顿。
G1收集器的阶段分以下几个步骤:
- 初始标记(它标记了从GC Root开始直接可达的对象);
- 并发标记(从GC Roots开始对堆中对象进行可达性分析,找出存活对象);
- 最终标记(标记那些在并发标记阶段发生变化的对象,将被回收);
- 筛选回收(首先对各个Regin的回收价值和成本进行排序,根据用户所期待的GC停顿时间指定回收计划,回收一部分Region)。
五、相关知识点
1、HotSpot 的 Server 和 Client 两种模式
HotSpot 包括 Server 和 Client 两种模式的实现:
- Java HotSpot Client VM(-client),为在客户端环境中减少启动时间而优化;
- Java HotSpot Server VM(-server),为在服务器环境中最大化程序执行速度而设计。
比较:Server VM 启动比 Client VM 慢,运行比 Client VM 快。Server 模式的运行中,垃圾回收处理做的比较好一些。
2、设置回收器的参数
下面给出配置回收器时,经常使用的参数:
- -XX: UseSerialGC:在新生代和老年代使用串行收集器
- -XX: UseParNewGC:在新生代使用并行收集器
- -XX: UseParallelGC :新生代使用并行回收收集器,更加关注吞吐量
- -XX: UseParallelOldGC:老年代使用并行回收收集器
- -XX:ParallelGCThreads:设置用于垃圾回收的线程数
- -XX: UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS 串行收集器
- -XX:ParallelCMSThreads:设定CMS的线程数量
- -XX: UseG1GC:启用G1垃圾回收器
3、各类回收器总结
回收器 | 回收代 | 回收模式 | 回收算法 | 特点 |
---|---|---|---|---|
Serial | 新生代 | 单线程 | 标记-复制 | 简单高效,内存消耗少, 停顿时间能控制在一百毫秒内 |
ParNew | 新生代 | 多线程 | 标记-复制 | Serial 的多线程版本 |
Parallel Scavenge | 新生代 | 多线程 | 标记-复制 | 关注重点为吞吐量 |
Serial Old | 老年代 | 单线程 | 标记-整理 | 同 Serial 简单高效,内存消耗少 |
Parallel Old | 老年代 | 多线程 | 标记-整理 | 同 Parallel Scavenge 适合那些注重吞吐量和 CPU 资源敏感的场合 |
CMS | 老年代 | 多线程 | 标记-清除 | 并发收集、低停顿,但是会产生大量空间碎片、并发阶段会降低吞吐量 |
G1 | 多线程 | 标记-复制 | 同时兼顾年轻代和老年代,针对于大 heap |
垃圾回收器的选用决定因素:应用程序的场景、硬件的制约 以及 吞吐量的需求。
- 串行垃圾回收是最简单的也是效率最低的,如果只是控制台的单线程程序,简单任务,并且机器配置不高,推荐使用。
- 并行垃圾回收器是 64bit server 默认的垃圾回收器,一般我们工作和生产上默认不配置,都是并行垃圾回收。对于一般的不要求吞吐的应用,并且硬件资源不是太充足的情况下,并行垃圾回收器差不多能满足需求。
- CMS 垃圾回收器是对并行垃圾回收器的一个优化,它以 CPU 和系统资源为代价,换取 GC 的延迟。不会一 GC 就 STW,而是根据情况 STW。一定程度上是资源换取速度。
- G1 垃圾回收器是针对于大 heap 的垃圾回收器,如果 heap 分配的足够大,分的 region 的优先级回收策略会优先清理垃圾多的 region 并且减少了内存空间碎片,分配大对象时不会因为无法找到连续内存空间而提前触发下一次 GC。