垃圾收集、垃圾收集算法、垃圾收集器
文章目录
-
1、垃圾收集(GC)
-
1.1 需要GC的内存区域
-
1.2 GC的对象
-
- 1.2.1 引用计数法与可达性分析法
-
1.2.2 双次标记法
- 1.3 什么时候触发GC
- 1.4 GC做了什么事
-
-
2、垃圾收集器
-
- 2.1 标记-清除器(Mark-Sweep)
-
- 2.2 标记-压缩器(Mark-Compression)
-
- 2.3 复制器(Copy Operation)
-
- 2.4 代际回收器(Generational Collection)
-
3 垃圾收集系统
-
-
Serial 收集器、Serial Old 收集器
-
ParNew 收集器
-
Parallel 收集器
-
- Parallel Scavenge 并行收集者
-
-
Parallel Old 并行旧收集者
-
3.4 CMS收集器
-
- 3.4.1 执行步骤
-
3.4.2 优缺点
-
- CMS收集器优点
- CMS收集器缺点
-
3.4.3 使用场景
- 3.5 G1收集器
-
- 3.5.1 执行步骤
-
3.5.2 优缺点
-
3.5.3 使用场景
-
-
-
3.6 CMS收集器与G1收集器的区别
-
- 3.6.1 区别一:应用领域存在差异
- 3.6.2 区别二:STW的时间长短不一
- 3.6.3 区别三:垃圾碎片程度存在明显差异
- 3.6.4 区别四:垃圾回收的具体流程存在不同之处
-
1、垃圾收集Garbage Collection(GC)
每个程序员在编程过程中都可能遇到内存溢出的问题。由于程序运行时内存资源是有限制的,在适当的时候必须有策略地清除不再被使用的对象以避免占用过多内存空间。这正是垃圾回收机制(GC)的主要职责。要深入理解垃圾回收机制(GC),可以从以下几个关键问题入手:首先确定"GC作用于哪些对象";其次明确"被回收的对象是什么类型的数据结构";再次弄清楚"什么时候启动垃圾回收过程";最后总结"垃圾回收机制具体执行了哪些操作步骤"。
1.1 需要GC的内存区域
在JVM中, 程序计数器.虚拟机栈.本地方法栈都伴随线程一起生成并随之消失, 栈帧通过方法进入和退出时进行入栈与出栈操作, 从而实现了高效的内存回收机制.因此, 我们的内存垃圾回收主要集中在java堆和方法区中, 在程序运行期间, 这部分内存的分配与使用是根据具体情况动态管理的.
Java堆:
- 在虚拟机启动时创建并供多线程使用 ,存储对象实例及其数组结构,
- 其中Eden区用于临时存储刚创建的对象实例,

1.2 GC的对象
1.2.1 引用计数法、可达性分析法
待回收的对象即为已死亡的对象;为了检测对象是否存活通常采用两种方法:引用计数法和可达性分析法
待回收的对象即为已死亡的对象;为了检测对象是否存活通常采用两种方法:引用计数法和可达性分析法
- 引用计数:每个实体都配备一个引用计数器,在进行新引用时将其计数值增加1;在执行引用释放操作后将其计数值减少1;当某个实体的引用计数值归零时,则表示该实体能够进行回收处理。这一方法相对简单,在处理相互循环引用的问题方面则显得力有未逮。
- 可达性分析(Reachability Analysis):从GC Roots出发进行遍历探索,在遍历过程中经过的所有节点和边都被认为构成了引用来连接这些节点的路径系统。如果某个实体与GC Roots之间没有任何引用来连接,则该实体被认为是无法到达的。尽管某些实体被判定为无法到达的对象仍然有可能会被回收处理的前提条件是它们必须经历至少两次完整的标记检查流程,并且只有在经过这两轮检查后仍然未能获得有效的利用价值时才最终确认为其可回收的对象。
在Java语言中,GC Roots包括:
* 虚拟机栈中引用的对象。
* 本地方法栈中引用的对象
* 方法区中类静态属性实体引用的对象。
* 方法区中常量引用的对象。
* 本地方法栈中JNI引用的对象。
可达性分析

虽然Object5至Object7之间有联系, 但它们无法到达GC根节点. 这些无法到达GC根节点的对象也并非无法单独存活, 它们仅仅被视为可回收对象.
1.2.2 两次标记
在可到达性分析方法中无法直接访问的目标对象并非无法立即删除,在这种情况下它们处于一个缓存期状态必须经历两次标记操作才可能真正删除
- 在可达性分析法中处理不可达的对象时,在首次标记后会实施一次筛选操作。具体而言,在此过程中需要考虑以下条件:此对象是否有必要调用finalizer方法。
- 当某个对象未覆盖finalizer方法实现(即finalizer方法尚未由虚拟机执行),或finalizer方法已经被虚拟机调用过时(后者意味着最终化操作已完成),虚拟机将在这些情况下认为无需再次执行finalizer方法,并进而立即实施第二次标记。
- 在重新定义finalizer方法的情况下(假设该方法此前未曾被执行过),该目标类会被放入一个队列中:
- 这个目标类与引用链中的任何其他类建立了关联关系,则不会参与回收过程;
- 反之如果未建立关联关系,则进而直接实施第二次标记。

1.3 什么时候触发GC
- 程序调用System.gc()时可以触发
- 系统自身来决定GC触发的时机
GC又包括minor garbage collection(简称m.g.c.)以及major garbage collection(简称MGC)两类形式。\n\n在Eden区满载的情况下发生minor garbage collection。\n\nmajor garbage collection的发生条件是Eden区装满且old generation内存不足。\n
- a.调用System.gc()时,系统建议执行Full GC,但是不必然执行
- b.老年代空间不足
- c.方法区空间不足
- d.通过Minor GC后进入老年代的对象大小 大于 老年代的可用内存
在Eden区和From Space区向To Space区复制的过程中,在所处理的对象规模超出To Space可用内存容量时,则会将该对象转移至旧内存区域,并确保旧内存区域的容量不足该对象规模
1.4 GC做了什么事
GC负责回收内存中的对象,并整理内存的工作
2、垃圾收集算法
常用的GC算法包括:标记-清除(Mark–Sweep)方法、标记-压缩(Mark–Compact)方法、复制(Copying)方法以及分代收集(Generational Collection)方法。值得注意的是,在商用JVM中如HotSpot等主流版本采用了基于分代收集机制的设计方案
2.1 标记-清除(Mark-Sweep)算法
- 标记-清除算法分为两个阶段:
- 第一部分属于标记环节,在这一部分中为每个目标对象设置存活标志字段(用于记录每个目标对象的状态信息),并完成存活状态的判断。
- 第二部分涉及垃圾回收操作,在这一部分中将已标记为非存活的目标对象移除,并完成垃圾回收流程。
优点:标记清除法中为每个存活的对象只需识别出一条引用链接即可,并且一旦发现该引用存在,则可立即判断该对象为存活状态。同时该方法无需移动任何对象的位置信息
缺点:运行效率较低(主要涉及递归处理与全堆对象扫描)。所有存活的对象均需在标记期间逐一处理;整个清除过程需对所有对象进行扫描以确保无遗漏。由于未移动对象的存在而导致内存碎片。内存碎片数量过多可能会影响系统资源管理,
具体表现为当需要为大型对象预留空间时难以找到可用位置,
从而促使系统提前执行垃圾回收操作。

2.2 标记-压缩(Mark-Compact)算法
- 标记-压缩法算法:
- 第一步是分类阶段,在此过程中, 该算法将所有对象划分为活体与死体两类。
- 在第二步中, 该算法没有直接处理死亡对象, 而是将活体对象归整至另一个区域, 然后彻底移除了其余对象。
- 优点:该算法能够有效避免与标记-清除算法相比产生的大量碎片空间。
- 缺点:如果存活的对象数量过多,则在整理阶段将导致相应的复制操作数量显著增加以提高效率。

2.3 复制(Copying)算法
- 复制算法:
该算法均将内存均分为两个区域,并在每次运行时仅在一个区域内执行相关操作;当该区域的内存已满时,则会将当前区域中的存活对象全部复制到相邻的另一个区域,并清空当前区域继续执行上述操作;
与基于标记-整理策略的算法相比...其主要区别在于,
该算法不是在同一区域内进行对象复制...`
优点:该方法易于实现并避免了内存碎片的产生;缺点:在每次运行过程中总会有一半的内存处于空闲状态而导致可用的内存空间仅占原来的一半

2.4 分代收集(Generational Collection)算法
基于分代策略的垃圾 collector 算法已成为现代 JVM 体系中广泛采用的技术方案。通常会将内存空间划分为老年代与新生代。在垃圾 collector 过程中,老年代仅需回收少量对象。新生代在垃圾 collector 过程中则面临大量对象待回收的问题。从而可以根据各代特征选择最合适的收集策略。
大多数垃圾收集器在处理新生代时会采用特殊策略以减少内存占用。由于在每次垃圾回收过程中都需要复制大量对象以实现内存复用的目的,因此所需复制操作次数相对较低。然而,实际运行中并不严格按照1:1的比例划分新生代的空间,通常是将新生代区域划分为一个较大的Eden区域和两个较小的Survivor区域,每次循环利用Eden区域与其中一个Survivor区域进行交互操作。当执行回收操作时,将Eden区域与未被使用的那个Survivor区域内存活的对象复制到另一个Survivor区域内,随后清理掉Eden区域以及上一轮使用的那个Surv survivor区。
然而由于老年代的特性是每次回收的对象数量有限 并且通常采用的是标记-压缩算法

3、垃圾收集器
若将收集算法视为内存回收的体系,则垃圾收集器可被视为内存回收的具体方案。
3.1 Serial 收集器、Serial Old 收集器
这两个收集器属于单线程设计,并且在执行垃圾回收操作时需要暂停所有非主线程。然而其优点在于实现上非常高效且易于理解,在性能上有较高的表现优势;然而其缺点在于可能会让用户等待较长的时间,在实际使用体验上存在一定的不便。
- Serial 收集器专为新生代设计,并基于Copying算法的工作原理实现垃圾回收机制,在执行回收操作时必须中断所有用户线程以确保系统的稳定性。
- Serial Old 收集器专为老年代设计,并基于Mark-Compact算法的工作原理实现垃圾回收机制,在执行回收操作时必须中断所有用户线程以确保系统的稳定性。

3.2 ParNew 收集器
- -XX:+:ParNewGC(其中new指新生代),适用于新生代的复制算法。
- ParNew收集器作为Serial收集器的多线程版本,在运行过程中也采用复制算法这一核心特性。
- 新生代在执行垃圾回收操作时会自动暂停所有用户线程以确保系统的稳定性。

3.3 Parallel 收集器
3.3.1 Parallel Scavenge 收集器
- 并行Scavenge收集器作为新一代多线程收集器,在回收过程中无需中断其他用户线程的运行,并采用基于复制的算法进行垃圾回收操作。相比ParNew收集器具有显著区别,在此方案中其主要目标是实现可调节的吞吐量水平。
3.3.2 Parallel Old 收集器
Parallel Old收集器属于Parallel Scavenge系列中的老年代版本(并行版本),它在整个回收过程中无需中断其他用户线程,并采用Mark-Compact算法。
3.4 CMS收集器
3.4.1 执行步骤
基于并行标记清除策略设计的Concurrent Mark Sweep收集器其主要目标是优化垃圾回收阶段的时间效率该方案采用了经典的Mark-Sweep回收算法具体来说在实际运行过程中该方案的操作流程主要包括以下几个关键步骤首先会扫描当前堆栈中的对象引用然后识别出哪些对象已经是存活状态接着将所有未存活的对象进行标记最后完成标记后的清理工作以释放内存空间
- CMS 初始标志(CMS initial mark):独自占用 PUC 资源,在此状态下会导致所有相关线程立即暂停执行;仅对能够直接关联到 CMS 的根对象进行打标处理,并且该过程非常迅速
- CMS 并行标志(CMS concurrent mark):该过程与当前用户的主线程同时运行;借助 GCRoots Tracing 技术为系统中所有的对象打上标志标签;这一阶段由于涉及大量数据处理而耗时较长
- CMS 重标(CMS remark):再次独自占用 CPU 资源,在此状态下会导致所有相关联的对象被强制停止运行;针对在 CMS 并行标志阶段生成的垃圾对象进行重标修正工作;这一过程通常会比初始打标阶段稍显拖沓一些
- CMS 并行清空(CMS concurrent sweep):该过程仍然与用户的主线程保持同步;通过分析已有的打标结果快速识别并清除掉所有的垃圾对象


3.4.2 优缺点
CMS收集器优点
- CMS 收集器具备高并发能力, 其原因在于其工作流程允许 GC 工作线程与用户线程实现并行操作。
- 该机制通过优化两次短暂的暂停(初始标记与重新标记)的时间长度, 实现了整体过程能在极短时间内完成, 并从而实现近乎平行化的目标。
CMS收集器缺点
- CMS收集器对CPU资源的依赖性较大,在并发运行阶段虽然不会导致用户线程出现停滞现象
但由于会占用一定量的CPU资源
因此在系统资源紧张的情况下容易出现明显的性能瓶颈 - CMS收集器无法有效处理浮动垃圾(Floating Garbage)。在执行'并发清理'步骤时
用户线程也会同时生成一部分可回收对象
但这些对象仅能在下一次清理操作完成之后才能被回收。 - CMS收集器采用的是基于标记-清除算法的工作机制,在完成清理操作后会产生显著数量的内存碎片。
当内存空间不足以支持新对象的创建或老年代升级操作时就会触发FullGC过程。
3.4.3 使用场景
该方法专注于最小垃圾回收间隔(即低停顿时间),在老年代GC操作不频繁的情况下表现更为理想。
3.5 G1收集器
G1收集器是当前技术发展走在前列的重要成果之一。该设备是专为服务端应用设计的专业化数据采集工具,在高性能计算环境中表现卓越。作为一个既能实现并行又能实现并发的数据采集设备,在设计上就充分考虑了系统的可扩展性和稳定性需求。基于此特点,在运行过程中系统能够有效预测和控制数据采集的时间间隔点。
3.5.1 执行步骤
G1对堆空间进行了重新定义,并将其划分为若干个规模相等且相互独立的区域(Region)。尽管仍然保留了新生代与老年代的基本概念,但新生代与老年代不再保持物理隔离状态,并均构成一部分Region集合(无需连续)。为避免进行全堆扫描操作,G1引入了一种名为Remremembered Set的数据结构,用于管理相关对象的引用信息,在GC根节点枚举范围内动态维护Remremembered Set即可实现既不进行全堆扫描操作又确保不会出现遗漏情况

我们之前已经提到过,在处理每个Region时G1都会遇到相互引用的问题为了彻底清理所有Region中的垃圾G1不得不执行一次全堆扫描这会显著影响垃圾回收的整体效率因此HotSpot采用了Remembered Set这一机制来管理对象之间的引用依赖关系这样一来就不会因为一次全堆扫描而导致内存泄漏的问题通过参考Remembered Set中的对应信息定位到相应的区域来进行清理这样一来就不会遗漏任何一个区域

Initial标记:仅标记GC Roots可以直接关联的对象,并且调整TAMS(Next Top at Mark Start)的值以实现以下目标:在并发执行下一阶段中的用户程序时,在正确的可用Region中动态创建新对象。为此阶段所需 pause thread的时间极为短暂。
并存标记(Concurrent Marking)是一种用于内存管理的技术:它从GC Roots节点开始执行可达性扫描以识别存活的内存对象,并将这些存活对象保留在内存中以便后续处理。(在并存标记过程中如果检测到某个区域内的所有内存对象均为垃圾对象那么该区域将立即被回收。此外在整个并存标记过程中系统会动态监控每个区域的内存使用情况即计算出该区域内存活的内存对象所占的比例)
最终标记(Final Marking):在修正并发标记过程中,在用户程序持续运行时导致标记发生变化的部分记录数据(类似于一个人在整理垃圾时另一个在随意丢弃),虚拟机会将此时间段内对象状态的变化信息记录至线程Remembered Set Logs中。最终标记阶段需将Rememered Set Logs中的数据整合回Rememered Set中。该阶段需暂停线程处理以确保同步性的同时仍可通过多线程机制进行优化处理。
采用筛选回收机制(Live Data Counting and Evacuation):首先按照各区域(Region)的回收价值与成本进行排序,并基于用户的预期垃圾收集(GC)时长来规划回收策略。同时可以在不影响用户程序运行的情况下实现这一阶段的操作——但因仅选择若干区域进行数据清理,则清理周期由用户提供控制权。此外通过暂停用户体验相关的线程将显著提升整体清理效率。
3.5.2 优缺点
- 并行与并发:G1能够充分通过多核CPU缩短停顿时间。虽然某些其他收集器在执行GC操作时需暂停Java线程以完成程序运行。然而,在这种情况下G1收集器依然能够采用并发机制让Java程序持续运行。
- 分代收集:G1采用了不同于CMS的方式管理不同分代内已创建对象以及新创建对象的回收过程。
- 空间整合:与基于标记-清除算法的CMS不同,G1采用了标记-整理算法进行整体性设计,而在细节层面则借鉴了复制算法的思想。无论是在全局层面上还是局部层面,G1始终避免了内存空间碎片问题,回收后能提供整洁有序的可用内存空间。这一特点使得程序能够在长时间运行中合理分配大对象,无需因缺乏连续内存而提前触发GC。
- 控制回收垃圾时间(可预测的停顿):减少停顿时间是G1和CMS共同的目标,但不同于这些传统收集器,G1不仅能够精确控制回收垃圾的时间长度还建立了合理的模型选择适当的Region作为目标区域从而实现即时回收
3.5.3 使用场景
- 实时数据所占比例达到了堆空间使用量的一半以上
- 数据对象分配和提升的速度变化显著
- 希望能够减少可能导致频繁的垃圾回收(GC)中断(时间跨度在0.5至1秒之间)
3.6 CMS收集器和G1收集器的区别
3.6.1 区别一: 使用范围不一样
- CMS收据器被配置为老年阶段的收据器,并可与新阶段中的Serial与ParNew两种类型收据机协同工作。
- G1收据机覆盖了老年阶段与新阶段,并无需与其他类型收据机协同工作。
3.6.2 区别二: STW的时间
- CMS收集器是基于最小垃圾回收停顿时间这一目标而设计的一种优化型数据清理装置。
- G1收集器具备预测能力,在垃圾回收周期中建立并维护一个可靠的停顿间隔模型。
3.6.3 区别三: 垃圾碎片
- CMS收集器采用了"标记-清除"算法进行垃圾回收,并可能导致内存碎片的产生。
- G1收集器采用了"标记-整理"算法,并实现了空间整合以降低内存空间碎片。
3.6.4 区别四: 垃圾回收的过程不一样

参考:
参考:https://www.cnblogs.com/igoodful/p/8727241.html
参考:https://zhuanlan.zhihu.com/p/161204689
参考:https://www.jianshu.com/p/bb544a3f5d71
参考:https://www.cnblogs.com/hhyangga/p/11718170.html
