JVM 垃圾回收器
文章目录
前言
Jvm的垃圾收集器(serial收集器、parnew收集器、parallel scavenge收集器、serial old 收集器、parallel old收集器、cms收集器、g1收集器)
- 新生代收集器:Serial、ParNew、Parallel Scavenge
- 老年代收集器:CMS、Serial Old、Parallel Old
- 整堆收集器: G1
收集方式
- 并行收集:指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态。
- 并发收集:指用户线程与垃圾收集线程同时工作(不一定是并行的可能会交替执行)。用户程序在继续运行,而垃圾收集程序运行在另一个CPU上。
一:Serial 收集器
特点:
Serial== 串行,也就是说它以串行的方式执行,单线程、简单高效(限定单个CPU的环境来说),Serial(串行)收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。收集器进行垃圾回收时,必须暂停其他所有的工作线程,直到它结束 STW(Stop The World)。
运行示例图:
二:ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本。除了使用多线程外其余行为均和Serial收集器一模一样。
特点:
多线程、ParNew收集器默认开启的收集线程数与CPU的数量相同,在CPU非常多的环境中,可以使用
-XX:ParallelGCThreads参数来限制垃圾收集的线程数。和Serial收集器一样存在STW问题。
ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器,因为它是除了Serial收集器外,唯一一个能与CMS收集器配合工作的。
运行示例图:
三:Parallel Scavenge 收集器(吞吐量优先收集器)
特点:
属于新生代收集器 ,采用复制算法的收集器,并行的多线程收集器。该收集器的目标是达到一个可控制的吞吐量。
GC自适应调节策略:Parallel Scavenge收集器可设置**-XX:+UseAdptiveSizePolicy参数。当开关打开时不需要手动指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等,虚拟机会根据系统的运行状况收集性能监控信息,动态设置这些参数以提供最优的停顿时间和最高的吞吐量**,这种调节方式称为GC的自适应调节策略。
四:Serial Old 收集器
Parallel Old是Parallel Scavenge收集器的老年代。
特点:单线程收集器,采用标记-整理算法
五:Parallel Old 收集器
Parallel Old是Parallel Scavenge收集器的老年代版。
特点:多线程,采用标记-整理算法。
应用场景:注重高吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge+Parallel Old 收集器。
六:CMS收集器
一种以获取最短回收停顿时间为目标的收集器。
特点:基于标记-清除算法实现。并发收集、低停顿。
应用场景:适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来更好的体验等场景下。如web程序、b/s服务。
CMS收集器的GC周期主要由7个阶段组成,其中有两个阶段会发生STW,其他阶段都是并发执行的。
阶段一: Initial Mark(初始化标记)
主要工作是标记GC Roots可直达的存活对象。
主要标记过程:
- 从GC Roots遍历可直达的老年代对象
- 遍历被新生代存活对象所引用的老年代对象
特点:
-
支持单线程或并行标记
-
发生STW,暂停所有应用线程
阶段二: Concurrent Mark(并发标记)
在该阶段,GC线程和应用线程将并发执行。也就是说,在第一个阶段(Initial Mark)被暂停的应用线程将恢复运行。
并发标记阶段的主要工作是,通过遍历Initial Mark标记出来的存活对象,继续递归遍历老年代,并标记可直接或间接到达的所有老年代存活对象。
由于在并发标记阶段,应用线程和GC线程是并发执行的,因此可能产生新的对象或对象关系发生变化,例如:
- 新生代的对象晋升到老年代
- 直接在老年代分配对象
- 老年代对象的引用关系发生变更
对于这些对象,后面需要重新标记以防止被遗漏(漏标)。为了提高重新标记的效率,本阶段会把这些发生变化的对象所在的Card标识为Dirty,这样后续就只需要扫描这些Dirty Card的对象,从而避免扫描整个老年代。
阶段三: Concurrent Preclean(并发预清理)
该阶段将会重新扫描前一个阶段标记的Dirty对象,并标记被Dirty对象直接或间接引用的对象,然后清除Card标识。
- 标记被Dirty对象直接或间接引用的对象
- 清除Dirty对象的Card标识
阶段四: Concurrent Abortable Preclean(可中止的并发预清理)
本阶段尽可能承担更多的并发预处理工作,从而减轻在Final Remark阶段的stop-the-world。
在该阶段,主要循环的做两件事:
- 处理 From 和 To 区的对象,标记可达的老年代对象
- 扫描处理Dirty Card中的对象
阶段五: Final Remark(重新标记)
预清理阶段是并发执行的,并不一定是所有存活对象都会被标记,因为在并发标记的过程中对象及其引用关系还在不断变化中。
因此,需要有一个STW的阶段来完成最后的标记工作,这就是重新标记阶段(CMS标记阶段的最后一个阶段)。主要目的是重新扫描之前并发处理阶段的所有残留更新对象。
主要工作:
- 遍历新生代对象,重新标记(新生代会被分块,多线程扫描)
- 根据GC Roots,重新标记
- 遍历老年代的Dirty Card,重新标记。这里的Dirty Card,大部分已经在Preclean(并发预清理)阶段被处理过了
阶段六: Concurrent Sweep(并发清理)
并发清理阶段,主要工作是清理所有的死亡对象,回收被占用的空间。
阶段七: Concurrent Reset(并发重置)
并发重置阶段,将清理并恢复在CMS GC过程中的各种状态,重新初始化CMS相关数据结构,为下一个垃圾收集周期做好准备