关于垃圾收集器你了解多少?一文总结七大垃圾收集器

简介: 如果说垃圾收集算法是内存回收的理论,那么垃圾收集器就是内存回收的具体实现。垃圾收集器目前存在的有很多,但是依旧没有哪个收集器是万能的存在,我们只能选择一个最适合应用的收集器。

微信搜索《Java鱼仔》,你离大厂更近了


每天一个知识点


关于垃圾收集器你了解多少,本文较长,建议先收藏后看


(一)概述


如果说垃圾收集算法是内存回收的理论,那么垃圾收集器就是内存回收的具体实现。

垃圾收集器目前存在的有很多,但是依旧没有哪个收集器是万能的存在,我们只能选择一个最适合应用的收集器。


下面会介绍目前主流Java虚拟机中所采用的七种垃圾收集器: Serial、parNew、ParallelScavenge、SerialOld、ParallelOld、CMS、G1 上述垃圾收集器有些适用于新生代,有些适用于老年代,有些在新生代和老年代都适应。如下图所示,连线表示可以配合使用。


网络异常,图片无法展示
|


(二)Serial


Serial是一个单线程的收集器,Serial的特点是它在进行垃圾收集时,必须“Stop the World”,意思就是当这个垃圾收集器开始工作时,必须停止其他所有的工作线程。听起来似乎很不靠谱,但是对于限定单个CPU的场景下,这种方式简单而高效。对于简单的桌面应用,分配给虚拟机的内存不会很大,对于一两百兆的新生代,Serial的垃圾收集时间可以控制在一百毫秒以内,对于用户来说基本上是无影响的。


Serial收集器在新生代使用复制算法


(三)ParNew


ParNew垃圾收集器是Serial的多线程版本,使用多条线程进行垃圾收集。除此之外,和Serial基本相同,ParNew在多线程收集垃圾时依旧需要**“Stop the World”**。


ParNew可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数量。


ParNew收集器在新生代使用复制算法


(四)Parallel Scavenge


Parallel Scavenge也是新生代收集器,也同样是多线程的收集器,但是和ParNew不同,Parallel Scavenge收集器关注的是一个可控制的吞吐量(Throughput)。所谓吞吐量指的是CPU用于运行代码的时间和CPU总消耗的时间比例。


吞吐量=运行代码的时间 /(运行代码的时间+垃圾收集时间)


理论上吞吐量越高,用户就越不能感受到停顿时间。


Parallel Scavenge提供了两个参数用来控制吞吐量:-XX:MaxGCPauseMillis和**-XX:GCTimeRatio**


-XX:MaxGCPauseMillis设置内存回收花费时间最高毫秒值,但是不要一味地认为只要把值设置很小,垃圾回收就更快了。这个停顿时间是以牺牲吞吐量和新生代空间换来的。


-XX:GCTimeRatio表示垃圾收集时间占总时间的比例,(1~100),也就是吞吐量的倒数。默认这个值是99,就是允许最大百分之1的垃圾手机时间(1/(1+99))。


还有一个参数**-XX:+UseAdaptiveSizePolicy**,打开这个参数后,就不需要自己设置新生代大小、晋升老年代对象年龄等参数,因此Parallel Scavenge收集器也被叫做吞吐量优先垃圾收集器。


Parallel Scavenge采用复制算法。


(五)Serial Old


一听名字就知道这是Serial收集器的老年代版本,是单线程收集器,采用标记-整理算法,其余的和新Serial基本相同。


(六)Parallel Old


Parallel Scavenge收集器的老年版本,多线程收集器,采用标记-整理算法,也是吞吐量优先。


(七)CMS收集器


CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。CMS是基于标记-清除算法的老年代垃圾回收器,CMS是目前应用最广泛的老年代垃圾回收器,它进行垃圾回收分为以下四步:


1、初始标记:标记GC Roots可以直接关联到的对象,速度很快(stop the world)

2、并发标记:根搜索算法的过程


3、重新标记:为了修正并发标记期间,因程序运行导致标记产生变动的对象。(stop the world)


4、并发清除:清除垃圾


这个过程中耗时最长的是并发标记和并发清除的过程,但是并不会stop the world,而初始标识和重新标记的速度都很快,即使stop the world也不会占用太多时间。


它的优点就是并发收集、并发清除、低停顿


但是它有三个显著的缺点:


1、对CPU资源十分敏感,因为并发标记和并发清除都是和程序同时运行,因此会占用CPU导致应用程序变慢。


2、无法处理浮动垃圾,浮动垃圾就是在并发清除过程中新生成的垃圾,这部分垃圾CMS无法在本次被清理,可能出现Concurrent Mode Failed报错,因此需要预留一定的内存空间,无法等到老年代快被占满时再清除。默认情况下,CMS在老年代使用了68%后就会被激活。可以设置-XX:CMSInitiatingOccupancyFraction设置这个值。


3、产生空间碎片,由于采用的是标记-清除算法,那就无法避免会产生空间碎片的问题,这会给分配大对象带来困难。


(八)G1


上面的垃圾回收器基本上都是按新生代和老年代去区分,但是G1不一样


堆结构


G1的堆结构就是把一整块内存区域划分为多个固定大小的块,JVM一般把堆划分为2000个region,然后每个region从1M到32M不等。


内存的分配


所有的region会被划分为Eden、Survivor、Old和Humongous,其中对Eden、Survivor和Old的理解用其他垃圾回收器去理解,这里多了一种类型Humongous,这个类型主要用来存储比标准块大百分之50或者更大的对象。


G1中的YGC


第一次YGC时,Eden块中存活的对象会被转移到一个或多个survivor块中,存活时间达到阈值,这些对象就会晋升到老年代。年轻代 GC 通过多线程并行进行。


此时会有一次 stop the world暂停,会计算出 Eden大小和 survivor 大小,用于下次young GC。统计信息会被保存下来,用于辅助计算size。比如暂停时间之类的指标也会纳入考虑。


一旦发生一次新生代回收,整个新生代都会被回收(根据对暂停时间的预测值,新生代的大小可能会动态改变)


G1中的老年代垃圾收集


老年代回收不会回收全部老年代空间,只会选择一部分收益最高的 Region,回收时一般会搭便车——把待回收的老年代 Region 和所有的新生代 Region 放在一起进行回收,这个过程一般被称为 Mixed GC


G1中的老年代垃圾收集和CMS收集器很相似


1、初始标记:附加在正常的YGC过程中,标记所有的根。(stop the world)


2、扫描根区域:扫描Survivor Regions中指向老年代的被初始标记标记的引用及引用的对象,这个阶段是并发执行的,但是在年轻代GC发生之前必须完成。(stop the world)


3、并发标记:在整个堆中查找活着的元素,此阶段可被YGC打断


4、再次标记:类似CMS的重新标记,处理并发标记阶段产生的新的对象引用,这阶段使用了SATB(snapshot-at-the-beginning)算法,该算法比CMS中所采用的快很多。(stop the world)


5、清理阶段:G1 GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域进行清理。(stop the world)


你可以发现,有四个阶段都需要stop the world,为了降低stop the world的时间,G1使用了RSet(Remembered Set)来记录不同代之间的引用关系。


RSet


RSet记录了“谁引用了我”,RSet记录了以下两种引用:


1、老年代 Region 间的引用


2、老年代 Region 到新生代 Region 的引用,Young GC 时直接将这种引用加入 GC Roots。


RSet的工作原理是这样的,进行 Young GC 时,选择新生代所在的 Region 作为 GC Roots,这些 Region 中的 RSet 记录了老年代->新生代的的跨代引用(「谁引用了我」),从而可以避免了扫描整个老年代。进行 Mixed GC 时,「老年代->老年代」之间的引用,可以通过待回收 Region 中的 RSet 记录获得,「新生代->老年代」之间的引用通过扫描全部的新生代获得(前面提到过 Mixed GC 会搭 Young GC 的便车),也不需要扫描全部老年代。总之,引入 RSet 后,GC 的堆扫描范围大大减少了。


(九)总结


上面这七种垃圾收集器中最优秀的非G1莫属,但是它还不够好,第一个原因是RSet会占用一定的内存,第二个原因是stop the world时间太长了。目前有两款更新的垃圾回收器:ZGC/C4 垃圾回收器Shenandoah 垃圾回收器,有兴趣的小伙伴可以去了解下。



相关文章
|
3月前
|
算法 安全 Java
Java内存管理:深入理解垃圾收集器
在Java的世界里,内存管理是一块基石,它支撑着应用程序的稳定运行。本文将带你走进Java的垃圾收集器(GC),探索它是如何默默守护着我们的内存安全。我们将从垃圾收集的基本概念出发,逐步深入到不同垃圾收集器的工作机制,并通过实例分析它们在实际应用中的表现。文章不仅旨在提升你对Java内存管理的认识,更希望你能通过这些知识优化你的代码,让程序运行更加高效。
58 3
|
15天前
|
存储 监控 算法
Java内存管理深度剖析:从垃圾收集到内存泄漏的全面指南####
本文深入探讨了Java虚拟机(JVM)中的内存管理机制,特别是垃圾收集(GC)的工作原理及其调优策略。不同于传统的摘要概述,本文将通过实际案例分析,揭示内存泄漏的根源与预防措施,为开发者提供实战中的优化建议,旨在帮助读者构建高效、稳定的Java应用。 ####
31 8
|
6月前
|
存储 算法 Java
技术笔记:JVM的垃圾回收机制总结(垃圾收集、回收算法、垃圾回收器)
技术笔记:JVM的垃圾回收机制总结(垃圾收集、回收算法、垃圾回收器)
61 1
|
6月前
|
算法 Oracle Java
垃圾收集器
复制算法和标记算法都是基于分代收集理论来的。
40 0
|
7月前
|
存储 Java
软件体系结构 - 垃圾收集器(1)分代ZGC
【4月更文挑战第22天】软件体系结构 - 垃圾收集器(1)分代ZGC
174 2
|
7月前
|
存储 缓存 算法
五、垃圾收集基础
五、垃圾收集基础
60 3
|
7月前
|
算法 Java
jvm性能调优 - 09白话新生代垃圾回收算法
jvm性能调优 - 09白话新生代垃圾回收算法
69 0
|
机器学习/深度学习 算法 Java
大牛用十年功力带你彻底理解JVM垃圾回收器:ZGC,回收设计
ZGC的并发回收算法采用的也是“目的空间不变性”的设计,关于目的空间不变性的更多内容可以参考第7章。 在第7章中提到,Shenandoah从JDK 13开始也采用“目的空间不变性”的设计。但是ZGC与Shenandoah相比,还是有不少细节并不相同,如表8-3所示。
|
存储 算法 Java
JVM 中的垃圾回收算法有啥门道吗?
JVM 中的垃圾回收算法有啥门道吗?
74 0
|
算法 安全 Java
深入剖析垃圾收集器之后,我发现里面没有扫帚
深入剖析垃圾收集器之后,我发现里面没有扫帚
120 0
深入剖析垃圾收集器之后,我发现里面没有扫帚