官方文档:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html
一、初识垃圾回收
1.确定垃圾对象
可达性分析
:通过GC Root对象向下寻找,查看某个对象是否可达,不可达则是垃圾对象。
GC Root
:Thread、类加载器、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等可以作为GC Root。
2.垃圾回收名词解释
Minor GC
:年轻代的垃圾回收叫做Minor GC。
Major GC
:老年代的垃圾回收叫做Major GC。
FULL GC
:通常情况下我们认为Minor GC + Major GC 是FULL GC,但严格意义上来说还需要加上Metaspace GC 才叫做FULL GC。
STW
:Stop-The-World机制简称为STW,即当jvm执行垃圾回收的时候会停止我们所有的业务线程。
二、垃圾收集算法
1.标记-清除(Mark-Sweep)
标记
:找出内存中需要回收的对象,并把他们标记起来。
堆中的所有对象都会被扫描一遍,才能确定需要回收的对象,比较耗时。
清除
:清除需要被回收的对象,释放对应的内存空间。
标记清除之后会产生大量的不连续的内存空间碎片,空间碎片太多会导致以后在程序运行过程当中需要分配大对象时,无法找到足够的连续内存空间,从而提前触发另外一次垃圾回收。
2.复制(Copying)
将内存划分为两块,每次只用其中一块
当其中一块内存不能再为新的对象分配内存空间,就将存活的对象复制到另一块空间,并且将使用过的内存空间全部清除掉。
缺点
:空间利用率低。同一时间只用到了一半的内存空间。
3.标记-整理(Mark-Compact)
标记过程仍然和"标记-清除"算法一样,但是后续步骤不是对可回收对象进行清除,而是让所有存活对象向一端移动,然后将存活对象边界以外的内存全部清除。
让所有存活对象向一端移动,清理掉边界以外的对象。
4.分代算法使用
Young区
:复制算法(一般对象在分配之后,生命周期较短,将少量的存活对象从Eden区
复制到Survivor区
)
Old区
:标记-清除或标记-整理算法(Old区对象存活时间比较长,复制会降低效率,做标记再清除或整理效率会更高)
三、垃圾收集器
1.Serial
serial收集器是最基本、发展历史最悠久的收集器,在jdk1.3.1之前是虚拟机年轻代垃圾回收的唯一选择。
串行收集器使用一个线程执行所有垃圾收集工作,这使得它相对高效,因为线程之间没有通信开销。它最适合单处理器机器,因为它不能利用多处理器硬件,尽管对于具有小数据集(大约100 MB)的应用程序,它在多处理器上也很有用。串行收集器在某些硬件和操作系统配置上是默认选择的,或者可以使用-XX:+UseSerialGC选项显式启用。
优点:简单高效,拥有很高的单线程收集效率
缺点:回收过程需要暂停所有线程
算法:复制算法
分代:年轻代
2.ParNew
Serial收集器的多线程版本
优点:在多CPU硬件环境下,比Serial效率高
缺点:收集过程暂停所有应用程序线程,单CPU硬件环境下比Serial效率低
算法:复制算法
分代:年轻代
3.Parallel Scavenge
Parallel Scavenge收集器是一个年轻代收集器,它也是使用"复制"算法的收集器,又是并行的多线程收集
器,但是Parallel Scavenge更关注系统的吞吐量
。
4.Serial Old
Serial Old收集器是Serial收集器的老年代版本,也是一个单线程收集器,不同的是采用"标记-整理"算法。
5.Parallel Old
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,也是多线程,不同的是它使用的"标记-整理"算法。
5.CMS(Concurrent Mark Sweep)
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收
停顿时间
为目标的老年代收集器,采用"标记-清除"算法,所以会产生大量空间碎片,并发阶段会降低吞吐量。
执行流程
a.初始标记----标记GC Roots能关联到的对象----stop the world
b.并发标记----进行GC Root 追踪
c.重新标记----修改并发标记因用户程序变动的内容----stop the world
d.并发清理
6.G1(Garbage-First)
G1(Garbage-First)垃圾收集器是一种服务器风格的垃圾收集器,目标是具有大内存的多处理器机器。它是一个试图在实现高吞吐量的同时,再以高概率实现垃圾收集无
停顿时间
为目标的收集器,并同时使用于老年代和年轻代,使用"标记-整理"算法。JDK8成熟并推荐使用,JDK9以后默认垃圾收集器。
使用G1收集器时,Java堆的内存布局与就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。
执行流程
a.初始标记----标记GC Roots能关联到的对象----stop the world
b.并发标记----标记GC Roots可达性对象,并收集region存活对象信息
c.最终标记----标记在并发阶段因用户程序发生变化的对象----stop the world
d.筛选回收----对各region的回收价值和成本排序,根据用户期望GC停顿时间进行回收----stop the world
7.吞吐量和停顿时间
吞吐量
:运行用户代码时间/(运行用户代码时间+垃圾收集时间)
停顿时间
:垃圾收集器执行回收暂停用户线程的时间
这两个指标是评价一个垃圾收集器优势的标准,jvm调优同样会观察这两个指标。
8.垃圾收集器分类
串行收集器:Serial、Serial Old
只有一个垃圾回收线程执行,用户线程暂停。适用于内存较小的嵌入式程序。
并行收集器[吞吐量优先]:Parallel Scavenge、Parallel Old
多条垃圾回收线程并行工作,用户线程暂停。适用于后台处理耗CPU场景。
并发收集器[停顿时间优先]:CMS、G1
用户线程和垃圾回收线程同时执行(不一定并行,可能交替执行),更少的并以不停顿用户线程为目标的收集器。适用于多交互服务器的场景,如web。
官方选择垃圾收集器参考:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html#sthref28
开启使用垃圾收集器
串行:
-XX:+UseSerialGC
-XX:+UseSerialOldGC
并行:
-XX:+UseParallelGC
-XX:+UseParallelOldGC
并发:
-XX:+UseConcMarkSweepGC
-XX:+UseG1GC