1 前言
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商、版本的虚拟机所提供的垃圾收集器都可能会有很大差别,并且一般都会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器。
1.1 新生代、老年代垃圾收集器组合
2 Serial 收集器
2.1 概念
Serial 收集器是最基本的收集器,曾经(JDK1.3.1之前)是新生代唯一的收集器。这个收集器是一个单线程的收集器,但是“单线程”的意义并不是他只会使用一个CPU或一条线程去完成垃圾收集工作。更重要的是他在垃圾收集的时候,必需暂停其他所有的工作线程,直到它收集结束(STOP THE WORLD)。
2.2 运行示意图
2.3 应用场景
使用复制算法,位于新生代。应用场景于Client模式或单CPU环境。
2.4 优缺点
- 优点:单CPU环境下收集效率最高。
- 缺点:目前环境基本为多核CPU环境,效率低。存在STW。
3 ParNew 收集器
3.1 概念
ParNew 收集器收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余行为包括 Serial 收集器可用的所有控制参数、收集算法、Stop The world、对象分配规则、回收策略等都与Serial收集器完全一样,实现上这两种收集器也共用了相当多的代码。
3.2 运行示意图![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/1e9a665c5d5b8bdad3eddb41811fcf5b.png)
3.3 应用场景
使用复制算法,位于新生代。应用场景于Server模式。
3.4 优缺点
- 优点:并发执行GC。
- 缺点:单CPU效率不如Serial。存在STW。
4 Parallel Scavenge 收集器
4.1 概念
Parallel Scavenge 收集器是一个新生代收集器,采用复制算法,并且是多线程收集器。Parallel Scavenge 收集器的关注点与其他收集器不同,CMS等收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge 收集器的目标则是达到一个可控制的吞吐量(Throughput)。这里所谓的吞吐量是指CPU用于运行用户代码的时间与CPU总消耗时间的比值,既吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉了1分钟,那么吞吐量就是99%。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效的利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
4.2 运行示意图
4.3 应用场景
使用复制算法,位于新生代。应用场景于高吞吐量为目标,即减少垃圾收集时间,让用户代码获得更长的运行时间。当应用程序运行在具有多个CPU上,对暂停时间没有特别高的要求。
4.4 优缺点
- 优点:并发执行GC;高吞吐量为目标,即减少垃圾收集时间。
- 缺点:存在STW。
5 Serial Old 收集器
5.1 概念
Serial Old 收集器是 Serial 收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。这个收集器的主要也是在目前的JAVA的Client模式下的虚拟机使用。如果在Server模式下,它主要还有两大用途:一个是在JDK 1.5及之前的版本中与Parallel Scavenge收集器搭配使用,另外一个就是作为CMS收集器的后备预案。如果CMS收集器出现Concurrent Mode Failure,则Serial Old收集器将作为后备收集器。
5.2 运行示意图
5.3 应用场景
使用标记-整理算法,位于老年代。应用场景于 Client 模式或单 CPU 环境。Server 模式下在 JDK1.5 及之前,与 Parallel Scavenge 收集器搭配使用(JDK1.6 有 Parallel Old 收集器可搭配)。
5.4 优缺点
- 优点:单CPU环境下收集效率最高;简单高效。
- 缺点:目前环境基本为多核CPU环境,效率低。存在STW。
6 Parallel Old 收集器
6.1 概念
Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本,用于老年代的垃圾回收,但与 Parallel Scavenge 不同的是,它使用的是“标记-整理算法”。适用于注重于吞吐量及CPU资源敏感的场合。
6.2 运行示意图
6.3 应用场景
使用标记-整理算法,位于老年代。应用场景于 JDK1.6 及之后用来代替老年代的 Serial Old 收集器,特别是 Server 模式、多 CPU 情况以及在注重吞吐量以及CPU资源敏感的场景。
6.4 优缺点
- 优点:注重吞吐量。
- 缺点: CPU资源敏感。存在STW。
7 CMS 收集器
7.1 概念
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合这类应用的需求。
从名字中包含“Mark Sweep”就可以看出,CMS收集器是基于“标记一清除”算法实现的。
整个过程分为4个步骤,包括:
- 初始标记(CMS initial mark)
- 并发标记(CMS concurrent mark)
- 重新标记(CMS remark)
- 并发清除(CMS concurrent sweep)
7.2 运行示意图
7.3 应用场景
使用标记-清除算法,位于老年代。应用于以获取最短回收停顿时间为目标,与用户交互较多的场景。如常见WEB、B/S系统的服务器上的应用。
7.4 优缺点
- 优点:以获取最短回收停顿时间为目标。并发收集、低停顿。以给用户带来较好的体验。
- 缺点:产生大量内存碎片。需要更多的内存。对CPU资源非常敏感。无法处理浮动垃圾。
8 G1 收集器
8.1 概念
G1(Garbage-First)是一款面向服务端应用的垃圾收集器,JDK 7 Update4后开始进入商用。HotSpot开发团队赋予它的使命是未来可以替换掉JDK 1.5中发布的CMS收集器。之前提供的收集器都是仅作用于新生代或者是老年代,但是G1收集器可以作用于新生代和老年代,因为使用G1收集器是java heap的内存结构有很大的不同,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但是他们已经没有了物理上的隔阂了,它们都是region的一部分的集合。
如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为以下几个步骤:
- 初始标记(Initial Marking)
- 并发标记(Concurrent Marking)
- 最终标记(Final Marking)
- 筛选回收(Live Data Counting and Evacuation)
8.2 运行示意图
8.3 应用场景
使用多种算法,同时位于新生代与老年代。应用场景主要是面向服务端应用,针对具有大内存、多处理器的机器;最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案。
8.4 优缺点
- 优点:最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案;最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案;最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案。
- 缺点:内存要求高,使用记忆集占大量内存,维护记忆集带来更高的执行负载。
9 垃圾收集器对比
序号 | 名称 | 算法 | 分代 | 是否单线程 | 用户线程与GC线程是否支持并发 |
---|---|---|---|---|---|
1 | Serial | 复制 | 新生代 | 是 | 否 |
2 | Serial Old | 标记-整理 | 老年代 | 是 | 否 |
3 | ParNew | 复制 | 新生代 | 否 | 否 |
4 | Parallel Scavenge | 复制 | 新生代 | 是 | 否 |
5 | Parallel Old | 标记-整理 | 老年代 | 是 | 否 |
6 | CMS | 标记-清除 | 老年代 | 否 | 是 |
7 | G1 | 多种回收算法 | 新生代、老年代 | 否 | 是 |
序号 | 名称 | 使用场景 | 优缺点 | 参数 |
---|---|---|---|---|
1 | Serial | Client模式或单CPU环境 | 优:单CPU环境下收集效率最高;简单高效。缺:目前环境基本为多核CPU环境,效率低。存在STW | “-XX:+UseSerialGC”:添加该参数来显式的使用串行垃圾收集器; |
2 | Serial Old | Client模式或单CPU环境。Server模式下在JDK1.5及之前,与Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配) | 优:单CPU环境下收集效率最高;简单高效。缺:目前环境基本为多核CPU环境,效率低 | “-XX:+UseSerialGC”:添加该参数来显式的使用串行垃圾收集器; |
3 | ParNew | Server模式 | 优:并发执行GC。缺:单CPU效率不如Serial。存在STW | “-XX:+UseConcMarkSweepGC”:指定使用CMS后,会默认使用ParNew作为新生代收集器;"-XX:+UseParNewGC":强制指定使用ParNew;"-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同; |
4 | Parallel Scavenge | 高吞吐量为目标,即减少垃圾收集时间,让用户代码获得更长的运行时间。当应用程序运行在具有多个CPU上,对暂停时间没有特别高的要求 | 优:并发执行GC;高吞吐量为目标,即减少垃圾收集时间。缺:存在STW | “-XX:MaxGCPauseMillis"控制最大垃圾收集停顿时间;”-XX:GCTimeRatio" 设置垃圾收集时间占总时间的比率;"-XX:+UseAdptiveSizePolicy" |
5 | Parallel Old | JDK1.6及之后用来代替老年代的Serial Old收集器,特别是Server模式、多CPU情况下。在注重吞吐量以及CPU资源敏感的场景。 | 优:注重吞吐量。缺: CPU资源敏感;存在STW | “-XX:+UseParallelOldGC”:指定使用Parallel Old收集器 |
6 | CMS | 以获取最短回收停顿时间为目标;与用户交互较多的场景;如常见WEB、B/S系统的服务器上的应用 | 优:以获取最短回收停顿时间为目标;并发收集、低停顿;以给用户带来较好的体验。缺:产生大量内存碎片;需要更多的内存;对CPU资源非常敏感;无法处理浮动垃圾。 | “-XX:+UseConcMarkSweepGC”:指定使用CMS收集器 |
7 | G1 | 面向服务端应用,针对具有大内存、多处理器的机器;最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案 | 优:最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案;最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案;最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案。缺:内存要求高,使用记忆集占大量内存,维护记忆集带来更高的执行负载 | “-XX:+UseG1GC”:指定使用G1收集器;"-XX:InitiatingHeapOccupancyPercent":当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45;"-XX:MaxGCPauseMillis":为G1设置暂停时间目标,默认值为200毫秒;"-XX:G1HeapRegionSize":设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region |
10 引用
- 《深入理解Java虚拟机 JVM高级特性与最佳实践》-周志明著