第一章 Java核心篇之JVM探秘:内存模型与管理初探
第二章 Java核心篇之JVM探秘:对象创建与内存分配机制
第三章 Java核心篇之JVM探秘:垃圾回收算法与垃圾收集器
第四章 Java核心篇之JVM调优实战:Arthas工具使用及GC日志分析
目录
前言
一、垃圾收集算法
引用计数法 (Reference Counting)
标记清除算法 (Mark-Sweep)
复制算法 (Copying)
标记整理算法 (Mark-Compact 或 Mark-Sweep-Compact)
分代算法 (Generational)
增量回收算法 (Incremental Garbage Collection)
二、垃圾收集器
(1)Serial Collector(串行收集器)
(2)Parallel Collector(并行收集器)
(3)ParNew Collector(并行新生代收集器)
(3)CMS 收集器
(4)G1 收集器
(5)ZGC (Z Garbage Collector)
三、垃圾收集底层算法
(1)三色标记
示例
(2)颜色指针
总结
前边我们已经了解过了JVM的内存模型与内存分配机制,在Java的世界里,内存管理是一项至关重要的任务。与C或C++等语言不同,Java自动处理对象的创建和销毁,这一过程主要由Java虚拟机(JVM)中的垃圾回收器(GC)来完成。本文将深入探讨垃圾回收算法与垃圾收集器的细节,旨在为读者提供一个全面且深入的理解。
垃圾回收(Garbage Collection, GC)是现代编程语言运行环境中的一个重要特性,它自动管理内存的分配和释放,避免了程序员手动管理内存可能引入的错误。以下是一些常见的垃圾回收算法及其特点:



虽然我们对各个收集器进行比较,但并非为了挑选出一个最好的收集器。更加没有万能的收集器,开发者应根据具体应用场景选择合适的GC算法,并通过调优提高系统效率。随着JVM的不断演进,未来的垃圾收集器将更加智能和高效。
描述:这是最简单的垃圾收集器,使用单线程执行垃圾回收操作。在执行GC时,所有其他线程都会暂停。
算法:年轻代使用复制算法,老年代使用标记-整理算法。
优势:消耗资源少,适用于小内存环境。
劣势:GC停顿时间长,不适合多核处理器。
适用场景:小内存、单核处理器的环境,或测试环境。
参数:-XX:+UseSerialGC -XX:+UseSerialOldGC(明确指定使用串行收集器)。
描述:
算法:
优势:
劣势:
适用场景:
参数:
-XX:+UseParallelGC(年轻代)、-XX:+UseParallelOldGC(老年代)。描述:ParNew是Parallel Collector的年轻代版本,它使用多线程进行垃圾收集。
算法:使用复制算法。
优势:多线程可以减少GC停顿时间,适合多核处理器。
劣势:与Serial Collector相比,消耗更多系统资源。
适用场景:多核处理器、需要与CMS Collector配合使用的情况。
参数:-XX:+UseParNewGC(启用ParNew收集器)。
描述:CMS Collector旨在最小化停顿时间,它在标记阶段与应用程序线程并发运行。
算法:使用标记-清除算法,但在清除阶段可能会暂停应用程序。
优势:低停顿时间,适合交互式应用。
劣势:可能导致内存碎片,不适合大堆内存。
适用场景:需要低停顿时间、对用户响应敏感的应用。
参数:
-XX:ConcGCThreads:并发的GC线程数
-XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(减少碎片)
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次
-XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
-XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,
JVM仅在第一次使用设定值,后续则会自动调整
-XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,降低CMS GC标记阶段(也会对年轻代一起做标记,如果在
minor gc就干掉了很多对垃圾对象,标记阶段就会减少一些标记时间)时的开销,一般CMS的GC耗时 80%都在标记阶段
-XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW
-XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;
描述:
G1是一种基于分区的垃圾收集器,它可以预测停顿时间,并尝试将停顿时间保持在一个可接受的范围内,即使在大堆内存中也是如此。
优点:均衡停顿时间和内存碎片,适合大堆内存。
缺点:配置复杂度较高,需要更多试验才能找到最佳配置。
使用场景:适合需要低停顿时间和大堆内存的企业级应用。
参数:
描述:
ZGC是一款JDK 11中新加入的具有实验性质的低延迟垃圾收集器,ZGC可以说源自于是Azul System公司开发的C4(Concurrent Continuously Compacting Collector) 收集器。
优点:极低的停顿时间,适合需要极低延迟的大数据应用。
缺点:新特性,可能在某些环境中稳定性未知。
使用场景:适合超大堆内存和对延迟敏感的应用。
参数:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC(开启ZGC收集器)。
三色标记算法是垃圾回收器(Garbage Collector, GC)中用于并发标记阶段的一种技术,主要用于解决在并发环境中准确标记存活对象的问题。这种算法由三种不同的“颜色”表示对象的不同状态,分别是白色、灰色和黑色。
在并发标记算法中,三色标记的作用在于帮助垃圾回收器追踪和标记所有可达的对象,同时处理在标记过程中由于其他线程修改对象引用而可能产生的漏标或错标问题。
下面是三色标记的基本概念:
白色(White):表示尚未被访问或标记的对象。在垃圾收集开始时,所有的对象都被认为是白色的。
灰色(Gray):表示已经开始访问但其引用还未完全检查的对象。当一个对象首次被发现并且其引用列表尚未被检查时,它会被标记为灰色。
黑色(Black):表示已经完全访问和标记的对象。一旦一个对象的所有引用都被检查完毕,并且确定它是可达的,那么它就会被标记为黑色。
在并发标记过程中,如果一个新的引用指向了一个白色的对象,那么这个对象会被标记为灰色,以确保它能被正确地加入到待检查的队列中。此外,当一个灰色对象的引用被访问时,如果这个引用指向的是白色对象,那么这个对象也会被标记为灰色,从而保证了所有可达对象都能被正确标记。
为了防止在并发标记阶段出现的数据竞争和不一致性,垃圾收集器会使用诸如读写屏障等技术来确保引用的正确更新和对象状态的同步。
三色标记算法是CMS和G1等现代垃圾收集器中使用的关键技术之一,它有助于实现高效、低中断的垃圾收集过程。
假设现在有白、灰、黑三个集合(表示当前对象的颜色),其遍历访问过程为:
需要注意,传统标记方式发生Stop The World时,对象间的引用是不会发生变化的,可以轻松完成标记。
而并发标记在标记期间应用线程还在继续跑,对象间的引用可能发生变化,就会出现错标和漏标的情况就有可能发生。

颜色指针(Colored Pointers)是一种用于辅助并发标记阶段的技术,它的设计目的是为了在不中断应用程序执行的情况下,高效地进行垃圾收集,同时避免数据竞争和标记错误。
在传统的并发标记算法中,如果多个线程同时访问同一对象,或者在标记过程中对象的引用发生变化,就可能产生标记不一致的情况,即所谓的“漏标”和“错标”。为了避免这些问题,ZGC引入了颜色指针的概念。
颜色指针包含两部分信息:
在ZGC中,颜色标记有两种状态:
ZGC如何利用颜色指针:
颜色指针技术允许ZGC在并发标记阶段安全地处理对象引用的变化,确保所有存活对象能够被正确标记,同时最小化了对应用程序执行的干扰。这种机制使得ZGC能够在高并发环境下提供非常低的暂停时间和高效的内存管理。
需要注意的是,颜色指针的实现细节可能会因具体实现和处理器架构而异,例如在64位系统中,颜色位可能直接编码在指针值中,而在某些情况下,可能需要使用额外的元数据来存储颜色信息。

如上图所示,64位对象引用划分如下:
18位:未使用的位
1位:可最终确定
1位:重新映射
1位:标记1
1位:标记为0
42位:对象地址
前18位保留供将来使用。42位可以寻址高达4 TB的数据。。。
理解垃圾回收算法与垃圾收集器不仅有助于开发者编写更高效的代码,还能在遇到性能瓶颈时,提供有效的诊断和优化策略。随着JVM技术的不断进步,未来我们或许能看到更加智能和高效的垃圾回收机制。