收集算法是内存回收的⽅法论,垃圾收集器就是内存回收的具体实现。
没有万能的垃圾收集器,我们能做的就是根据具体应⽤场景选择适合⾃⼰的垃圾收集器。
收集器名称 | 简单介绍 | 线程 | 回收算法 | 优点 | 适用场景 | 备注 |
Serial(串行)收集器 | 最基本、历史最悠久的垃圾收集器。它的 “单线程” 的意义不仅仅意味着它只会使⽤⼀条垃圾收集线程去完成垃圾收集⼯作,更重要的是它在进⾏垃圾收集⼯作的时候必须暂停其他所有的⼯作线程("Stop The World" ),直到它收集结束。 | 单线程 | 新⽣代采⽤复制算法,⽼年代采⽤标记-整理算法。 | 简单⽽⾼效(与其他收集器的单线程相⽐)。Serial 收集器由于没有线程交互的开销,⾃然可以获得很⾼的单线程收集效率。 | 对于运⾏在 Client 模式下的虚拟机来说是个不错的选择。 | |
Serial Old 收集器 |
Serial 收集器的⽼年代版本 | 1. JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使⽤; 2.作为 CMS 收集 器的后备⽅案。 | ||||
ParNew 收集器 | Serial 收集器的多线程版本,⾏为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全⼀样。 | 多线程 |
它是许多运⾏在 Server 模式下的虚拟机的⾸要选择,除了 Serial 收集器外,只有它能与 CMS 收 集器(真正意义上的并发收集器,后⾯会介绍到)配合⼯作。 | |||
Parallel Scavenge 收集器 |
它看上去⼏乎和 ParNew ⼀样。 Parallel Scavenge 收集器关注点是吞吐量(⾼效率的利⽤ CPU )。 CMS 等垃圾收集器的关注点更多的是⽤户线程的停顿时间(提⾼⽤户体验)。所谓吞吐量就是 CPU 中⽤于运⾏⽤户代码 的时间与 CPU 总消耗时间的⽐值。 | 复制算法 |
Parallel Scavenge 收集器提供了很多参数供⽤户找到最合适 -XX:+UseParallelGC 使⽤ Parallel 收集器+ ⽼年代串⾏ -XX:+UseParallelOldGC 使⽤ Parallel 收集器+ ⽼年代并⾏ 的停顿时间或最⼤吞吐量,如果对于收集器运作不太了解,⼿⼯优化存在困难的时候,使⽤Parallel Scavenge 收集器配合⾃适应调节策略,把内存管理优化交给虚拟机去完成也是⼀个不错 的选择。 | 注重吞吐量以及CPU 资源的场合 | ||
Parallel Old 收集器 |
Parallel Scavenge 收集器的⽼年代版本。 | 标记-整理 | ||||
CMS(Concurrent Mark Sweep)收集器 |
⼀种以获取最短回收停顿时间为⽬标的收集器。它 ⾮常符合在注重⽤户体验的应⽤上使⽤。 | 标记--清理 |
HotSpot 虚拟机第⼀款真正意义上的并发收集器, 它第⼀次实现了让垃圾收集线程与⽤户线程(基本上)同时⼯作。 | |||
G1 收集器(Garbage-First) |
它具备⼀下特点: 并⾏与并发 : G1 能充分利⽤ CPU 、多核环境下的硬件优势,使⽤多个 CPU ( CPU 或者 CPU 核⼼)来缩短 Stop-The-World 停顿时间。部分其他收集器原本需要停顿 Java 线程执 ⾏的 GC 动作, G1 收集器仍然可以通过并发的⽅式让 java 程序继续执⾏。 分代收集 :虽然 G1 可以不需要其他收集器配合就能独⽴管理整个 GC 堆,但是还是保留了分代的概念。 G1 收集器的运作⼤致分为以下⼏个步骤: 初始标记 并发标记 最终标记 筛选回收 G1 收集器在后台维护了⼀个优先列表,每次根据允许的收集时间,优先选择回收价值最⼤的Region 。这种使⽤ Region 划分内存空间以及有优先 级的区域回收⽅式,保证了 G1 收集器在有限时间内可以尽可能⾼的收集效率(把内存化整为零)。 | 空间整合:G1 从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。 |
可预测的停顿 :这是 G1 相对于 CMS 的另⼀个⼤优势,降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还能建⽴可预测的停顿时间模型,能让使⽤者明确指定在⼀个⻓度为 M 毫秒的时间⽚段内。 | ⼀款⾯向服务器的垃圾收集器,主要针对配备多颗处理器及⼤容量内存的机器. 以极⾼概率满⾜ GC 停顿时间要求的同时,还具备⾼吞吐量性能特征,被视为 JDK1.7 中 HotSpot 虚拟机的⼀个重要进化特征。 | ||
ZGC 收集器 |
与 CMS 中的 ParNew 和 G1 类似,不过 ZGC 对该算法做了重⼤改进。 | 标记-复制 | 在 ZGC 中出现 Stop The World 的情况会更少 |
并⾏和并发概念补充:
并⾏( Parallel ) :指多条垃圾收集线程并⾏⼯作,但此时⽤户线程仍然处于等待状态。
并发( Concurrent ) :指⽤户线程与垃圾收集线程同时执⾏(但不⼀定是并⾏,可能会交
替执⾏),⽤户程序在继续运⾏,⽽垃圾收集器运⾏在另⼀个 CPU 上。
这是 JDK1.8 默认收集器
使⽤ java -XX:+PrintCommandLineFlags -version 命令查看
JDK1.8 默认使⽤的是 Parallel Scavenge + Parallel Old ,如果指定了 -XX:+UseParallelGC 参
数,则默认指定了 -XX:+UseParallelOldGC ,可以使⽤ -XX:-UseParallelOldGC 来禁⽤该功能
-XX:InitialHeapSize=262921408 -XX:MaxHeapSize=4206742528 -
XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -
XX:+UseCompressedOops -XX:+UseParallelGC
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode) 它的
运作过程相⽐于前⾯⼏种垃圾收集器来说更加复杂⼀些。整个过程分为四个步骤:
初始标记: 暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快 ;
并发标记: 同时开启 GC 和⽤户线程,⽤⼀个闭包结构去记录可达对象。但在这个阶段结
束,这个闭包结构并不能保证包含当前所有的可达对象。因为⽤户线程可能会不断的更新引
⽤域,所以 GC 线程⽆法保证可达性分析的实时性。所以这个算法⾥会跟踪记录这些发⽣引
⽤更新的地⽅。
重新标记: 重新标记阶段就是为了修正并发标记期间因为⽤户程序继续运⾏⽽导致标记产⽣
变动的那⼀部分对象的标记记录,这个阶段的停顿时间⼀般会⽐初始标记阶段的时间稍⻓,
远远⽐并发标记阶段时间短
并发清除: 开启⽤户线程,同时 GC 线程开始对未标记的区域做清扫。
从它的名字就可以看出它是⼀款优秀的垃圾收集器,主要优点: 并发收集、低停顿 。但是它有下
⾯三个明显的缺点:
对 CPU 资源敏感;
⽆法处理浮动垃圾;
它使⽤的回收算法 -“ 标记 - 清除 ” 算法会导致收集结束时会有⼤量空间碎⽚产⽣。