垃圾回收算法和垃圾收集器算是JVM的基础,也是Java的核心知识,Java程序员们都应该了解,并根据实际需要进行垃圾收集器的选择和JVM参数调优。
垃圾回收算法
- 常用的有三种垃圾回收算法,一些新版本jdk的算法暂时不在这里介绍。
- 新生代对象多又小,需要频繁迁移整理(而不是删除),一般采用复制算法
- 老年代对象大且变动不频繁,一般变动就清理,一般采用“标记-清除”算法或“标记-压缩”算法
标记-清除算法
- mark sweep,简称 MS
- 缺点是容易造成碎片化
- “标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段
- 首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
复制算法
- copying
- 缺点是浪费空间
- “复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。
- 当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
标记-压缩算法
- mark compare,简称 MC
- 标记-压缩算法,也称标记-整理算法
- 标记过程仍然与“标记-清除”算法一样
- 但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
垃圾收集器
新生代收集器:Serial、 ParNew 、 Parallel Scavenge
老年代收集器: CMS 、Serial Old、Parallel Old
整堆收集器: G1 , ZGC (不涉年代)。
从串行-并行,从几十M,到几个G,到几十G
Serils / Serils Old
- 单线程垃圾收集器
- Serial收集器(复制算法): 新生代单线程收集器,标记和清理都是单线程,优点是简单高效;
- Serial Old收集器 (标记-整理算法): 老年代单线程收集器,Serial收集器的老年代版本;
ParNew
- ParNew收集器 (复制算法): 新生代收并行集器
- 实际上是Serial收集器的多线程版本
- 在多核 CPU环境下有着比Serial更好的表现;
Parallel Scavenge / Parallel Old
- 多线程并行垃圾收集器
- JDK1.8默认默认垃圾收集器
- Parallel Scavenge收集器 (复制算法): 新生代并行收集器,追求高吞吐量,高效利用 CPU
- 吞吐量 = 用户线程时间/(用户线程时间+GC线程时间),高吞吐量可以高效率的利用CPU时间,尽 快完成程序的运算任务,适合后台应用等对交互相应要求不高的场景
- Parallel Old收集器 (标记-整理算法): 老年代并行收集器,吞吐量优先,Parallel Scavenge收 集器的老年代版本;
CMS
- concurrent mark swep 的缩写,即并发的标记-清除
- CMS(Concurrent Mark Sweep)收集器,使用标记-清除算法
- 老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。
- 三色标记 - 错标 - Incremental Update - Remark
G1
- JDK1.9默认垃圾收集器
- G1(Garbage First)收集器 ,使用标记-整理算法,是Java堆并行收集器
- G1收集器是JDK1.7提供的一 个新收集器,G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。
- 此外,G1回收集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。
- 1.9后默认的垃圾回收算法,特点保持高回收率的同时减少停顿。采用每次只清理一部分,而不是清理全部的增量式清理,以保证停顿时间不会过长。
- 其取消了年轻代与老年代的物理划分,但仍属于分代收集器,算法将堆分为若干个逻辑区域(region),一 部分用作年轻代,一部分用作老年代,还有用来存储巨型对象的分区.
- 同CMS相同,会遍历所有对象,标记引用情况,清除对象后会对区域进行复制移动,以整合碎片空间
- 年轻代回收: 并行复制采用复制算法,并行收集,会StopTheWorld
- 老年代回收: 会对年轻代一并回收
- 标记-清除过程
- 初始标记 完成堆root对象的标记,会StopTheWorld.
- 并发标记 GC线程和应用线程并发执行.
- 最终标记 完成三色标记周期,会StopTheWorld.
- 复制/清除 会优先对可回收空间加大的区域进行回收
ZGC
- Open JDK 11加入
- ZGC (Z Garbage Collector)是一款由Oracle公司研发的,以低延迟为首要目标的一款垃圾收集器
- 它是基于动态Region内存布局,(暂时)不设年龄分代,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的收集器。
- 在 JDK 11 新加入,还在实验阶段,主要特点是:回收TB级内存(最大4T),停顿时间不超过10ms。
- 优点:低停顿,高吞吐量, ZGC 收集过程中额外耗费的内存小。
- 缺点:浮动垃圾
垃圾收集器选择
- 我们平常的 Web 服务器,都是对响应性要求非常高的。选择性其实就集 中在 CMS 、 G1 、 ZGC 上。而对于某些定时任务,使用并行收集器,是一个比较好的选择。下面详细讲一下,仅供参考。
- 如果你的堆大小不是很大(比如 100MB ),选择串行收集器一般是效率最高的。
参数: -XX:+UseSerialGC 。 - 如果你的应用运行在单核的机器上,或者你的虚拟机核数只有单核,选择串行收集器依然是合适的,这时候启用一些并行收集器没有任何收益。
参数: -XX:+UseSerialGC 。 - 如果你的应用是“吞吐量”优先的,并且对较长时间的停顿没有什么特别的要求。选择并行收集 器是比较好的。
参数: -XX:+UseParallelGC 。 - 如果你的应用对响应时间要求较高,想要较少的停顿。甚至 1 秒的停顿都会引起大量的请求失败,那么选择 G1 、 ZGC 、 CMS 都是合理的。虽然这些收集器的 GC 停顿通常都比较短,但它需要一些额外的资源去处理这些工作,通常吞吐量会低一些。
参数: -XX:+UseConcMarkSweepGC 、 -XX:+UseG1GC 、 -XX:+UseZGC 等。