赞
踩
垃圾回收(Garbage Collection, GC)是Java虚拟机(JVM)的一个重要部分,它帮助开发者管理内存,确保程序能有效并且安全地运行。本文将介绍Java中几种常用的垃圾回收器,以及它们的工作原理和适用场景。
在Java中,对象是在堆内存上分配的,当这些对象不再被任何部分的应用所引用时,这些对象就被认为是垃圾。垃圾回收器的任务就是找到这些不再被需要的对象,并释放它们占据的内存空间,以供新对象使用。
无法控制内存是许多程序可能遇到的问题。例如,内存泄漏,这通常发生在程序不再需要的数据没有被及时清理的情况下,长此以往可能导致内存耗尽,最终程序崩溃。通过自动垃圾回收,Java试图避免这种情况。
Serial GC是最基本的垃圾回收器,适用于单核处理器的环境。它使用到的是单线程进行垃圾回收
,它在进行垃圾回收时会暂停所有的应用线程
("Stop-The-World"事件),这通常不适用于多线程的服务器环境,但可能适合于简单的命令行程序。特点:单线程、STW、复制和标记整理算法
使用参数:
-XX:+UseSerialGC
Parallel GC它使用多个线程
来执行垃圾回收,因此它比Serial GC更适合多核心服务器环境。其主要关注点是提高吞吐量
,缺点是在垃圾回收时仍然会有应用线程暂停STW
。在JDK 4中引入,在JDK 8中是默认垃圾回收器。特点:并发、STW、复制和标记整理算法
使用参数:
-XX:+UseParallelGC
CMS GC 最短停顿时间
为目标的垃圾回收器。它在回收内存的同时允许应用程序线程继续执行,适合于交互性强的应用,其中快速响应比吞吐量更重要。在JDK5中引入。特点:并发、较短STW、标记清除算法有内存碎片
使用参数:
-XX:+UseConcMarkSweepGC
初始标记
:GC为单线程,暂停所有用户线程,只标记GC Root 直接引用的对象并发标记
:多个GC线程和用户线程同时工作,从GC Roots开始遍历整个对象图,找出所有可达对象。重新标记
:由于并发标记阶段用户程序仍在运行,可能会导致部分对象的标记状态发生改变,重新标记阶段就是为了修正这些变动。并发清理
:多个GC线程并发清理那些在标记阶段被标记为垃圾(即未被标记为黑色的对象)的对象,释放内存空间。并发重置
:多个GC线程并发将存活对象上的标记给移除掉,避免影等下次gcG1 GC (Garbage-First)是一种服务器端垃圾回收器,它旨在填补Parallel GC和CMS GC之间的空白,提供一个低延迟的垃圾回收解决方案
。G1将堆内存分成多个不同的区域,并根据每个区域可能包含垃圾的大小来确定回收的优先顺序。在JDK 7 update 4中引入,JDK 9成为默认的垃圾回收器。特点:最短STW、并发、空间整理
使用参数:
-XX:+UseG1GC
初始标记
:GC单线程进行标记,暂停所有用户线程,记录GC ROOT 直接引用的对象并发标记
:多个GC线程和用户线程同时工作,从GC Roots开始遍历整个对象图,找出所有可达对象。最终标记
:因为上个过程是并发进行的,所以有些对象是在标记过程中新产生的,导致没有标记上,所以这个过程进行修正,将这些漏网之鱼都标上,这个过程是多线程 STW 进行。可以通过增量标记或使用写屏障技术来减少需要重新标记的对象数量。筛选回收
:计算每个region的回收成本,并按照回收成本进行排序,然后按照用户期望的停顿时间进行计算,决定应该回收哪些region。可以通过更智能的region选择算法来优化,例如优先回收垃圾对象最多的region或回收成本最低的region。ZGC是一种低延迟的垃圾回收器,目前仍在积极开发中。ZGC的设计初衷是为了在大堆内存上工作,并且几乎不产生延迟。这使得它非常适合需要快速响应但是内存占用大的应用程序。
使用参数:
-XX:+UseZGC
Shenandoah GC与ZGC有类似的目标:减少停顿时间,即便是在大堆或者多核心处理器的情况下。Shenandoah通过在GC的许多阶段与应用线程并发执行来实现这一目标。
使用参数:
-XX:+UseShenandoahGC
在选择垃圾回收器时,需要考虑应用程序的需求:
JDK 8(Java Development Kit 8)中默认的垃圾回收器组合为Parallel Scavenge(用于Young Generation)加上Parallel Old(用于Old Generation)
。Parallel GC 在 JDK 5 中开始成为默认的垃圾回收器,特点:并行+STW
。
Parallel Scavenge收集器是一个新生代垃圾收集器,它使用复制算法
,而且是并行的多线程收集器
。它主要关注于达到一个可控制的吞吐量(Throughput)。所谓吞吐量,就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即:吞吐量 = 用户代码时间 /(用户代码时间 + 垃圾收集时间)。服务器环境中,这种收集器能够最大化的利用CPU时间,尽快地完成程序的运算任务,特别适用于多核服务器。
Parallel Old收集器是Parallel Scavenge收集器的老年代版本
,使用多线程和"标记-整理"
算法。这个收集器是在JDK 6u14版之后引入的。从JDK 9 开始默认垃圾回收器是 G1 (Garbage-First)
CMS(Concurrent Mark-Sweep)收集器是一个以获取最短回收停顿时间为目标的收集器
,使用"标记-清除
"算法,并且是并发的。但是在JDK版本的迭代中没有任何一个版本使用CMS为一个默认的垃圾回收器。
选择Parallel Scavenge和Parallel Old的原因而不用CMS,通常基于以下考虑:
吞吐量优先: 如果应用不是非常注重服务响应时间,而是更希望在单位时间内完成更多的工作,即追求较高的吞吐量,那么Parallel Scavenge加上Parallel Old垃圾收集器会是一个更合适的选择。
碎片化问题:CMS使用的"标记-清除"算法容易导致内存碎片化,当碎片化严重到一定程度,CMS需要进行一次完全停顿的垃圾收集以整理内存碎片(Full GC),这可能导致较长时间的停顿。而Parallel Old使用的"标记-整理"算法可以有效避免内存碎片化问题。
更简单稳定: 相较于CMS,Parallel Scavenge和Parallel Old通常来说维护起来比较简单,参数配置也更容易。而CMS有几百个参数需要配置调整,对于维护的成本是非常高的。
随着Java 9的发布,G1(Garbage-First)垃圾收集器成为了默认的垃圾收集器,它旨在替代CMS,提供更好的性能表现和更简单的调优选项。而在Java 11之后,还引入了ZGC和Shenandoah等实验性的低停顿时间垃圾收集器。
CMS(Concurrent Mark Sweep)和G1(Garbage-First)是Java虚拟机中的两种不同的垃圾回收器,它们各自有不同的特点和适用场景。
初始标记
:GC为单线程,暂停所有用户线程,只标记GC Root 直接引用的对象并发标记
:多个GC线程和用户线程同时工作,从GC Roots开始遍历整个对象图,找出所有可达对象。重新标记
:由于并发标记阶段用户程序仍在运行,可能会导致部分对象的标记状态发生改变,重新标记阶段就是为了修正这些变动。并发清理
:多个GC线程并发清理那些在标记阶段被标记为垃圾(即未被标记为黑色的对象)的对象,释放内存空间。并发重置
:多个GC线程并发将存活对象上的标记给移除掉,避免影等下次gc初始标记
:GC单线程进行标记,暂停所有用户线程,记录GC ROOT 直接引用的对象并发标记
:多个GC线程和用户线程同时工作,从GC Roots开始遍历整个对象图,找出所有可达对象。最终标记
:因为上个过程是并发进行的,所以有些对象是在标记过程中新产生的,导致没有标记上,所以这个过程进行修正,将这些漏网之鱼都标上,这个过程是多线程 STW 进行。可以通过增量标记或使用写屏障技术来减少需要重新标记的对象数量。筛选回收
:计算每个region的回收成本,并按照回收成本进行排序,然后按照用户期望的停顿时间进行计算,决定应该回收哪些region。可以通过更智能的region选择算法来优化,例如优先回收垃圾对象最多的region或回收成本最低的region。随着Java虚拟机的发展,G1由于其更好的可预测性和适应性,越来越多地成为默认的垃圾收集器,尤其是在Java 9及以后的版本中。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。