JVM(四)垃圾回收
1 垃圾标记
1.2 引用计数算法:

1.2.1 示例


1.2.2 弊端
存在循环引用的问题


1.2 可达性分析算法:


GC root包含哪些?

2 GC算法
2.1 标记-清除算法
从gc root遍历所有对象,标记可达对象,即存活对象,然后再遍历所有的对象(过程为下图的蓝色横线),清除垃圾对象。


2.1.1 标记清除算法缺点
效率不高,因为需要从头遍历。清除的意思是,把上图黑色的垃圾对象的内存地址保存在空闲列表,下次有新的对象时,将黑色的块覆盖了,并不是将黑色块置空。

2.2 复制算法
准备B区,A区开始,从GC roots开始遍历,遍历到可达的对象复制到B区,S1区和S2区就用到了复制算法,S区的对象大多朝生夕死。


- 复制算法优点:没有标记清除过程,高效,复制后保证空间的连续性,没有碎片问题。
- 复制算法缺点:需要内存空间大,复制后需要维护新的引用关系。而且如果系统存活的对象多和垃圾少,需要复制很多对象,会使效率低。因此适用于新生代。
2.3 标记压缩算法



3 分代收集
分代收集是JVM垃圾回收的基础架构,其核心思想源于“弱代假说”:绝大多数对象朝生夕灭,少数对象长期存活。
3.1 实现方式
将Java堆划分为两个主要区域:
1、新生代(Young Generation):存放新创建的对象,存活率低,回收频繁。
采用复制算法:将存活对象复制到另一块Survivor区,清空原区,效率高、无碎片。
默认比例:Eden : Survivor1 : Survivor2 = 8 : 1 : 1。
2、老年代(Old Generation):存放熬过多次Minor GC的对象,存活率高。
采用标记-清除或标记-整理算法,避免频繁复制开销。
4 垃圾回收的相关知识点
4.1 内存溢出与内存泄漏

4.2 GC的串行、并发、并行:
GC的三种“工作模式”:
- 串行(Serial):单线程执行GC,暂停所有应用线程(Stop-The-World)
- 并行(Parallel):多线程并行执行GC,仍暂停应用线程
- 并发(Concurrent):GC线程与应用线程同时运行,不完全暂停应用
![image]()
![image]()
4.3
4.3.1 STW(STOP THE WORLD)


4.3.2 安全点与安全区域:
4.3.2.1 安全点

-
实现机制:采用主动式中断(Voluntary Suspension)
JVM设置一个全局“安全点标志”。
所有Java线程在执行到安全点时,主动轮询该标志。
若发现标志为真,线程自行挂起,等待GC等操作完成。 -
比如,线程执行到方法调用指令,就会停下来。
4.3.2.1 安全区域
当线程处于非运行状态(如 Thread.sleep()、Object.wait()、I/O阻塞、JNI调用)时,它无法主动执行代码,也就无法到达安全点。此时,安全点机制失效。
解决方案:引入安全区域
线程进入一段引用关系不会发生变化的代码段(如阻塞系统调用、JNI本地方法)时,会主动声明进入安全区域。
在此区域内,JVM无需等待该线程到达安全点,可直接将其视为“已暂停”。
当线程准备离开安全区域时,必须检查GC是否已完成:
若已完成 → 直接继续执行。
若未完成 → 阻塞等待,直到GC结束信号到达。
补充说明:

4.4 终结引用
5 垃圾回收器
5.1 GC的性能指标
重点看标红的三个,这三个不可能都满足。


5.1.1 吞吐量

5.1.2 暂停时间

5.1.3 示例

- 注重吞吐量:(200ms+200ms)/6000ms,总暂停400ms
- 主动低延迟:每次暂停100ms,总暂停500ms
5.1.4

5.2 垃圾回收器组合

5.3 Serial回收器
概述:单线程、串行执行的收集器。年轻代用Serial,老年代用Serial Old。仅使用一个CPU核心,所有GC操作(标记、清除、复制)均由单线程完成,执行垃圾回收时,暂停所有应用线程(Stop-The-World),直到GC结束才恢复程序运行;新生代使用复制算法(Copying),老年代使用标记-整理算法(Mark-Compact)。
- 使用 -XX:+UseSerialGC 显式启用的JVM

图示:

5.4 ParNew回收器
并行回收,该回收器就是Serial回收器的并行版本,STW时间较短

图示,新生代使用ParNew,老年代使用Serial Old

5.5 CMS回收器
CMS 专为对响应时间敏感的应用(如 Web 服务)设计,通过并发执行垃圾回收线程与用户线程,显著减少 STW 停顿。其采用标记-清除算法。
5.5.1 CMS工作原理:
- 初始标记(STW),仅仅标记GC Roots直接引用的对象(如栈帧变量、静态字段)(如栈引用、静态变量)。停顿时间极短
- 并发标记,从初始标记阶段标记好的的对象出发,遍历整个对象图,标记所有可达对象
- 重新标记(STW),由于并发标记阶段,用户线程是不停的、运行着的,这时可能会出现以下的情形,一,有的对象会创建出来,然后很快成为垃圾,,二,有的标记好的对象是存活的,用户贤臣将其变为不存活。因此需要重新标记。
- 并发清除,清理垃圾对象。

5.5.2 CMS缺点
-
内存碎片:使用“标记-清除”算法,不进行压缩,长期运行易产生碎片,可能导致Full GC 。可以使用以下参数来进行压缩,-XX:CMSFullGCsBeforeCompaction=N(默认值0),意为,每经历N次Full GC后,才执行一次碎片压缩
-
退化问题,若老年代空间不足或回收速度跟不上分配速度,会退化使用Serial Old进行Full GC,导致长时间停顿 。
-
浮动垃圾,并发清除期间用户线程仍可分配对象,这部分垃圾无法回收
-
CPU资源占用高:并发阶段需占用CPU资源,在CPU较少时影响明显。比如2核,1个核用来跑并发处理,1个核跑用户线程。
5.6 Parallel Scavenge 和 Parallel Old
Parallel Scavenge 是新生代的垃圾回收器,采用复制算法,专注于高吞吐量(Throughput)。
Parallel Old 是老年代的垃圾回收器,采用标记-整理算法,是 Parallel Scavenge 的老年代搭档。
java8默认的回收器。
高吞吐量
- 适合后台计算和批处理任务
-
- 数据批处理作业(如日志分析、报表生成)
-
- 科学计算或模拟任务
-
- 任务执行时间较长,允许偶尔的较长 GC 停顿,更关注整体任务完成时间,而非单次请求的响应延迟,例如:视频转码、大规模数据导入、定时统计任务等
5.6.1 原理之多线程并行处理

5.6.2 内存自适应
自适应调整
1、自动化调优:根据应用的实际运行情况,动态调整新生代、老年代的大小及晋升阈值等关键参数。
2、平衡吞吐量与停顿时间:在保证高吞吐量的前提下,尽可能控制垃圾回收的停顿时间。
5.6.3 自适应调整的工作原理
1、监控运行时数据
Parallel Scavenge 会持续收集垃圾回收过程中的关键指标,例如:对象分配速率、对象存活时间分布、每次 GC 的停顿时间、堆内存的使用效率。
2、基于监控数据,自动调整以下参数:
新生代与老年代比例:根据对象晋升模式优化内存划分。
晋升阈值:控制对象从新生代进入老年代的条件。
堆大小扩展/收缩:根据负载变化调整总堆容量。
2.3、反馈式优化
通过多次 GC 周期的数据反馈,逐步逼近最优配置,适应应用不同阶段的内存使用特征。
5.6.4参数
1、启用 Parallel Scavenge + Parallel Old 组合
-XX:+UseParallelGC 或-XX:+UseParallelOldGC
2、设置最大暂停时间
-XX:MaxGCPauseMillis:n ,设置每次垃圾回收时的最大停顿毫秒数
3、设置吞吐量
-XX:GCTimeRatio:n ,设置吞吐量为n,即=n/(n+1)。如n为99,就是99%的吞吐量
4、启用自适应调节策略。默认开启
-XX:+UseAdaptiveSizePolicy
- 最大暂停时间和吞吐量要平衡使用。比如极端情况下,最大暂停设置为1ms,吞吐量设置为99%,垃圾回收器很可能是办不到的。
5.7 G1收集器
5.7.1 介绍
1、分区Region:化整为零

2、H区举例,区域设置为4MB,有一个对象是7MB,那么就放H区。连续的H区,比如下图中占了三个格子的H区


3、某个E区被整个回收后,可以充当O区,但是某个区不能既是E区又是O区

4、优点

5.7.2 流程概述

5.7.3 执行流程之年轻代回收



- 会导致 Stop-The-World(STW)
选择某些区域的依据:

- 最多只能回收4个,因为5个的话,可能会超过200ms。要考虑余量

-
5.1 回收eden区与s区,存活的复制到另一个s区
![image]()
-
5.2 达到阈值的晋升老年代,没有达到的复制到另一个s区
![image]()

5.7.4 执行流程之混合回收

- 7.1 混合回收 (MixedGC)
![image]()
5.7.4 混合回收的过程
并发标记,从GC Roots出发,遍历存活的对象,并且标记
最终标记,漏标的对象是,并发标记时没有标记的对象(不存活的),因为被引用的关系导致存活了,要标记上;用户线程执行中,可能会新建对象,不理会,下次GC再处理;用户线程执行中,可能有的对象会不再存活了,也不理会,下次GC再处理。
并发复制清理,这个过程中,对于老年代来说,会选择存活度低的区域来回收(G1名字的由来)。存活度,比如有两个老年代区,第一个区100个对象存活1个(低存活度),第二个100个对象存活99个。这样会释放更多的内存。

混合回收图示:红色为存活度低的区域。s是survivor区,O是Old区

注意:







浙公网安备 33010602011771号