文章目录
- 一、Serial垃圾回收器
- 二、Parallel垃圾回收器
- 三、CMS垃圾回收器
- 四、G1垃圾回收器
- 4.1 G1垃圾回收器的步骤
- 4.2 根对象
- 4.3 G1垃圾回收器常用参数
- 五、ZGC垃圾回收器
- 六、Epsilon垃圾回收器
- 七、垃圾回收算法
- 7.1 标记–清除算法
- 7.2 复制算法
- 7.3 标记–整理算法
- 7.4 分代收集算法
垃圾回收器是Java虚拟机(JVM)中负责自动内存管理的组件,主要任务是回收不再使用的对象所占用的内存。JVM提供了多种垃圾回收器,每种都有其特点和适用场景。以下是几种常见的垃圾回收器及其执行步骤的介绍:
一、Serial垃圾回收器
Serial垃圾回收器是最基本的、历史最悠久的垃圾收集器。JDK1.3之前回收新生代唯一的选择。Serial垃圾回收器是一个单线程的收集器,它进行垃圾收集时必须暂停其他所有的工作线程,直到它收集结束。Serial 用于新生代回收,采用复制算法;Serial Old 用于老年代回收,采用标记-整理算法。
Serial 一般用在客户端程序或占用内存较小的微服务,因为客户端程序一般分配的内存都比较小,可能几十兆或一两百兆,回收时的停顿时间是完全可以接受的。而且 Serial 是所有回收器里额外消耗内存最小的,也没有线程切换的开销,非常简单高效。
二、Parallel垃圾回收器
Parallel垃圾回收器也被称为吞吐量优先的垃圾回收器(吞吐量是指CPU用于执行用户代码的时间和CPU总消耗时间的比值),其目标是达到一个可控制的吞吐量。Parallel Scavenge 用于新生代回收,采用复制算法;Parallel Old 用于老年代回收,采用标记-整理算法。
Parallel垃圾回收器采用自适应调节策略,高吞吐量可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。因此,Parallel垃圾回收器常见在服务器环境中使用。
JDK 8的默认垃圾回收器是Parallel Scavenge + Parallel Old,即使用Parallel Scavenge(新生代)+ Parallel Old(老年代)
三、CMS垃圾回收器
CMS(Concurrent Mark Sweep)垃圾回收器是一种老年代垃圾回收器,它可以在垃圾回收过程中与用户线程并发执行,主要牺牲了系统的吞吐量来追求收集速度,适合追求垃圾收集速度的服务器。
由于CMS使用标记-清除算法,会产生内存碎片,分配对象时内存不足,造成并发失败触发Full GC,这时老年代并发垃圾回收器会退化到Serial Old串行垃圾回收器,使用标记-整理算法并进行整理,整理至碎片减少,才能继续工作,但这会导致垃圾回收的整体时间变长。CMS已经在JDK9中被标记为废弃。
四、G1垃圾回收器
G1是一个并行与并发的标记-复制垃圾回收器,它的设计目标是为了适应不断扩大的内存和不断增加的处理器数量,进一步降低暂停时间(pause time),同时兼顾良好的吞吐量。在JDK 9及以后的版本中,G1垃圾回收器成为了默认的垃圾回收器。
G1回收器将堆内存划分为多个固定大小的Region,每个Region可以是年轻代或老年代的一部分。在进行垃圾回收时,G1可以同时使用多个GC线程,有效利用多核计算能力。
4.1 G1垃圾回收器的步骤
- 初始标记(Initial Mark):在此阶段,G1垃圾回收器会暂停所有应用线程(STW),标记出根对象直接关联的对象作为存活对象,并记录下需要处理的存活对象。
- 并发标记(Concurrent Marking):在此阶段,应用线程与垃圾回收线程并发执行。垃圾回收线程通过遍历对象图来标记出其他存活对象,并更新标记状态。
- 最终标记(Final Mark):在此阶段,G1垃圾回收器会再次暂停所有应用线程,完成并发标记阶段期间可能产生的新增存活对象的标记。
- 筛选回收(Live Data Counting and Evacuation):在此阶段,G1垃圾回收器会根据堆内存中的存活对象和空闲空间的大小进行筛选,选择性地回收那些最有价值的区域。它采用了一个优先级队列来决定哪些区域将被优先回收。
- 并发清理(Concurrent Cleanup):在此阶段,应用线程与垃圾回收线程并发执行。垃圾回收线程负责清理未使用的对象,并将空闲空间返回给堆。
- 对象复制(Object Copying):为了提高内存回收的效率,G1垃圾回收器采用了区域化的方式。在此阶段,它会将存活对象从一个或多个不连续的区域复制到其他空闲的区域中,以便整理出更大的连续空间。
- 更新引用(Update References):在此阶段,G1垃圾回收器会更新指向已复制对象的引用,使其指向新的地址。
- 重置状态(Reset State):在此阶段,G1垃圾回收器会恢复所有暂停的应用线程,继续正常的应用程序执行。
其中初始标记、最终标记、并发清理和对象复制这几个步骤是STW的
4.2 根对象
G1垃圾回收器会将以下对象作为根对象:
- 虚拟机栈(VM Stack)中的本地变量和参数:这些变量包含在方法调用期间创建的对象引用。
- 方法区中的静态变量(Static Variables):静态变量是类级别的变量,存储在方法区中,并且一直存在于整个程序的生命周期中。
- 常量池(Constant Pool)中的常量引用:常量池是存储字符串常量、符号引用等信息的特殊区域,其中的常量引用也可以作为根对象。
- 本地方法栈(Native Method Stack)中的本地变量和参数:与虚拟机栈类似,本地方法栈也包含了本地方法调用期间创建的对象引用。
- JNI全局引用:JNI全局引用(JNI Global Reference)是Java Native Interface(JNI)中一种特殊的引用类型,用于跨越Java和本地代码之间的对象引用。在使用JNI调用本地代码时,确保Java对象在本地代码执行期间不被垃圾回收器回收。
4.3 G1垃圾回收器常用参数
-XX:MaxGCPauseMillis:用于设置最大GC暂停时间的目标。默认值是200ms.
-XX:G1SurvivorRatio:用于设置Eden区与Survivor区的比例。默认值是8,survivor TO区:survivor FROM区:老年代 = 1:1:8
-XX:InitiatingHeapOccupancyPercent:启动并发GC周期时的堆内存占用百分比,默认值是45%。
五、ZGC垃圾回收器
ZGC(Z Garbage Collector)是一款在JDK 11中加入的低延迟的垃圾收集器,在JDK 15中成为具有商用的垃圾收集器。ZGC是一款并发的垃圾收集器,主要为了满足如下目标进行设计:
- 停顿时间不会超过10ms。
- 停顿时间不会随着堆的增大而增大。
- 可支持几百M,甚至几T的堆大小。
ZGC使用颜色指针进行标记对象的垃圾状态,当这个被指向的内存发生变化的时候,颜色也就会发生变化。
六、Epsilon垃圾回收器
Epsilon垃圾回收器控制内存分配,但是不执行任何垃圾回收工作。设计的目的是提供一个完全消极的GC实现,分配有限的内存分配,最大限度降低消费内存占用量和内存吞吐时的延迟时间
七、垃圾回收算法
7.1 标记–清除算法
标记–清除算法分为标记和清除两个阶段,标记阶段遍历内存区域,对需要回收的对象打上标记。清除阶段再次遍历内存,对已经标记过的内存进行回收。 缺点是容易产生大量内存碎片
7.2 复制算法
复制算法将内存划分为等大的两块,每次只使用其中的一块。当一块用完了,触发GC时,将该块中存活的对象复制到另一块区域,然后一次性清理掉这块没有用的内存。缺点是内存利用率不高,每次只能使用一半内存。
7.3 标记–整理算法
标记–整理算法分为标记阶段和整理阶段两个阶段:标记阶段与标记-清除算法一样,从根对象开始,遍历所有直接或间接引用的对象,将它们进行标记。整理阶段在回收不存活的对象占用的空间后,将所有的存活对象往一端空闲空间移动,并更新对应的指针。
标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。
7.4 分代收集算法
分代收集算法算法并没有新的内容,只是根据对象的存活周期的不同,将内存分为新生代和老年代。新生代中的对象存活时间较短,老年代中的对象存活时间较长。针对不同代采取不同的收集算法。