JVM GC垃圾回收算法

垃圾回收算法(GC Algorithms)

JVM 根据对象生命周期特性(分代假设)采用不同的回收算法,核心算法包括:

标记-清除(Mark-Sweep)

此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。
流程​​:标记所有存活对象 → 清除未标记对象。
​​优点​​:简单、无对象移动开销。
​​缺点​​:内存碎片化、效率较低(需遍历两次堆)。
在这里插入图片描述

复制算法(Copying)

此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。此算法每次只处理正在使用中的对象,因此复制成本比较小, 同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。
​流程​​:将存活对象复制到另一块内存区域 → 清空原区域。
​​优点​​:无碎片、效率高(适用于存活率低的对象,如新生代)。
​​缺点​​:内存利用率低(需预留一半空间)。
在这里插入图片描述

​​标记-整理(Mark-Compact)

此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
流程​​:标记存活对象 → 移动对象到内存一端 → 清理边界外内存。
​​优点​​:无碎片、内存利用率高。
​​缺点​​:对象移动开销大(适用于老年代)。
在这里插入图片描述

分代收集(Generational Collection)

核心思想​​:根据对象存活时间划分内存区域(新生代、老年代)。
​​新生代​​:存活率低 → 使用复制算法(如 Serial、ParNew)。
​​ 老年代​​:存活率高 → 使用标记-清除或标记-整理算法(如 CMS、G1)。

可达性算法(Reachability Analysis)

可达性算法(​​Reachability Analysis​​)是 JVM 垃圾回收的核心机制,用于判断对象是否存活。其核心思想是:​​从一组根对象(GC Roots)出发,遍历所有引用链,未被引用的对象视为可回收垃圾​​。以下是其详细原理、流程和应用场景:
在这里插入图片描述

可达性算法的基本流程​

1.​​确定 GC Roots​​
JVM 枚举所有根对象(如栈帧中的局部变量、静态变量等),作为遍历起点。
2.遍历引用链​​
从 GC Roots 出发,递归遍历所有直接或间接引用的对象,形成​​对象图(Object Graph)​​。
3.​​标记存活对象​​
所有被遍历到的对象标记为存活(如使用三色标记法中的黑色或灰色状态)。
4.​​回收不可达对象​​
未被遍历到的对象(白色对象)视为垃圾,由具体 GC 算法回收(如标记-清除、复制等)。

GC Roots 的具体类型​

以下对象被定义为 GC Roots,不会被回收:

虚拟机栈中的引用​​

当前执行方法的局部变量、方法参数(如 public void foo(Object param))。
当前线程的调用栈中所有方法的局部变量。

本地方法栈中的引用​​

JNI(Java Native Interface)方法中的对象引用(如通过 JNIEnv 调用的对象)。

方法区的静态变量和常量​​

类的静态变量(static 修饰)。
字符串常量池中的引用(如 String s = “abc”)。

同步锁持有的对象​​

通过 synchronized 关键字锁定的对象(如 synchronized(obj) 中的 obj)。

JVM 内部对象​​

系统类加载器(ClassLoader)加载的类。
异常对象(如 OutOfMemoryError)、线程对象等。

跨代引用记录对象​​

卡表(Card Table)中记录的老年代对新生代的引用(需特殊处理)。

引用类型对可达性的影响​

引用类型强引用(Strong)软引用(Soft)弱引用(Weak)虚引用(Phantom)
定义​默认引用(Object obj = new Object())内存不足时回收(SoftReference)下次 GC 必回收(WeakReference)仅用于跟踪对象回收(PhantomReference)
​​可达性影响​强可达软可达弱可达不可达
​​回收条件​不可达时回收内存不足时回收无论内存是否充足均回收对象回收后入队通知

示例:

Object strongRef = new Object();          // 强引用
SoftReference<Object> softRef = new SoftReference<>(new Object());
WeakReference<Object> weakRef = new WeakReference<>(new Object());
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), new ReferenceQueue<>());

三色标记(Tri-color Marking)

定义

用三种颜色抽象对象的状态:
​​白色(White)​​
​​初始状态​​:对象未被访问(未标记)。
​​最终回收​​:标记完成后仍为白色的对象视为可回收垃圾。
​​灰色(Gray)​​
​​中间状态​​:对象被标记为存活,但其子引用(成员变量、数组元素等)尚未被遍历。
​​黑色(Black)​​
​​最终存活状态​​:对象被标记为存活,且所有子引用已被遍历完成。

标记流程(基本步骤)​

​​初始阶段

所有对象设为白色。
将 GC Roots 直接引用的对象标记为灰色(放入灰色队列)。

标记阶段(并发)

从灰色队列中取出对象:
—遍历该对象的所有子引用,将子引用指向的白色对象标记为灰色。
—将当前对象标记为黑色。
重复上述过程,直到灰色队列为空。

​​最终阶段

所有存活对象应为黑色,白色对象视为垃圾。

并发标记的两种问题​

因用户线程与 GC 线程并发运行,对象引用可能发生变化,导致两种风险:

漏标(对象丢失)

场景​​:

黑色对象(已标记完成)被用户线程写入了一个新的白色对象引用。
用户线程删除了灰色对象到某个白色对象的引用。

结果​​:

白色对象未被标记为存活,导致被错误回收。
例​​:

初始:黑(A) → 灰(B) → 白(C) → 白(D)
B 即将处理,但此时用户线程执行:
1. A.field = D   // 黑对象引用了白对象
2. B.field = null // 断开灰对象到C的引用
最终:C未标记(白色),D未标记(白色),会被误回收。
解决
  1. 增量更新(Incremental Update)​​
    ​​原理​​:记录新插入的引用,重新标记。
    ​​实现​​:当用户线程将黑色对象插入对白色对象的引用时,通过​​写屏障(Write Barrier)​​ 将黑色对象重新标记为灰色。
    ​​示例 GC​​:CMS(Concurrent Mark-Sweep)。
    ​​2. 原始快照(SATB, Snapshot At The Beginning)​​
    ​​原理​​:基于标记开始时存在的对象引用关系快照(即假设这些对象是存活的)。
    ​​实现​​:当用户线程修改引用关系(如删除一个引用),通过写屏障将旧引用的目标对象标记为灰色。
    ​​示例 GC​​:G1、ZGC、Shenandoah。

错标(浮动垃圾)

场景​​:

对象实际已死亡,但在标记阶段被标记为存活。

解决:

​​可容忍​​:仅导致少量内存未及时释放,下次 GC 可清理。

OopMap(Ordinary Object Pointer Map)

OopMap 记录了栈上本地变量到堆上对象的引用关系。其作用是:垃圾收集时,收集线程会对栈上的内存进行扫描,看看哪些位置存储了 Reference 类型。如果发现某个位置确实存的是 Reference 类型,就意味着它所引用的对象这一次不能被回收。但问题是,栈上的本地变量表里面只有一部分数据是 Reference 类型的(它们是我们所需要的),那些非 Reference 类型的数据对我们而言毫无用处,但我们还是不得不对整个栈全部扫描一遍,这是对时间和资源的一种浪费。
一个很自然的想法是,能不能用空间换时间,在某个时候把栈上代表引用的位置全部记录下来,这样到真正 gc 的时候就可以直接读取,而不用再一点一点的扫描了。事实上,大部分主流的虚拟机也正是这么做的,比如 HotSpot ,它使用一种叫做 OopMap 的数据结构来记录这类信息。
我们知道,一个线程意味着一个栈,一个栈由多个栈帧组成,一个栈帧对应着一个方法,一个方法里面可能有多个安全点。 gc 发生时,程序首先运行到最近的一个安全点停下来,然后更新自己的 OopMap ,记下栈上哪些位置代表着引用。枚举根节点时,递归遍历每个栈帧的 OopMap ,通过栈中记录的被引用对象的内存地址,即可找到这些对象( GC Roots )。

OopMap(Ordinary Object Pointer Map)是 JVM 用于​​快速定位 GC Roots​​ 的关键数据结构,通过记录栈帧和寄存器中的对象引用位置,显著减少垃圾回收时的停顿时间(Stop-The-World, STW)。以下是其生成时机及作用机制的详细解析:

OopMap 的生成时机​

OopMap 的生成与 ​​JIT 编译器​​ 和 ​​安全点(Safe Point)​​ 密切相关,主要发生在以下场景:

方法编译时(JIT 阶段)​

​​即时编译(JIT)​​:
当方法被 JIT 编译器编译为本地机器码时,编译器会分析方法的栈帧布局,并生成对应的 OopMap。
​​记录内容​​:
栈帧中哪些位置(偏移量)存储了对象引用(Oop,Ordinary Object Pointer)。如:局部变量表、方法参数、this 指针等。
​​示例​​:
若方法的局部变量表第 3 个槽位是 Object obj,则 OopMap 会记录该槽位的偏移量。

安全点(Safe Point)​

​​安全点触发​​:当 JVM 需要执行 GC、代码反优化等操作时,所有用户线程必须暂停在安全点。
​​安全点位置​​:通常插入在方法调用、循环回边(如 for 循环)、异常抛出等位置(避免长时间不进入安全点)。
​​OopMap 更新​​:在安全点处,JVM 会生成或更新当前线程的 OopMap,确保准确记录此时栈帧和寄存器中的引用。

GC 仅是触发安全点的一种场景,其他操作(如偏向锁撤销)也会触发安全点。
​​并非所有 OopMap 都在 GC 前生成​​,但 GC 前必须依赖安全点更新 OopMap。

特定指令插入​

显式生成指令​​:JIT 编译器会在生成的机器码中插入特殊指令(如 test 指令),用于检查是否需要进入安全点并生成 OopMap。

OopMap 如何协助 JVM 获取 GC Roots​

GC Roots 是垃圾回收的起点,包括​​栈帧中的局部变量、静态变量、JNI 引用等​​。OopMap 的作用是快速枚举这些根引用,避免全栈扫描。

快速定位引用位置​

直接映射​​:OopMap 明确记录了栈帧中哪些位置存储了对象引用(如局部变量、方法参数)。
​​寄存器记录​​:部分引用可能存储在寄存器中(如 this 指针),OopMap 也会记录这些寄存器的名称。

结合安全点减少 STW 时间​

​​暂停线程​​:当 GC 触发时,所有线程需快速暂停在安全点。
​​遍历 OopMap​​:GC 线程直接读取各线程的 OopMap,遍历记录的引用位置,收集所有 GC Roots。
​​无需全栈扫描​​:避免逐字节检查整个栈内存,极大缩短暂停时间。

与卡表(Card Table)协作​

维护跨代引用​​:卡表用于记录老年代到新生代的引用,OopMap 帮助快速定位这些引用所在的栈或寄存器位置。
​​写屏障支持​​:当用户线程修改对象引用时,写屏障会更新卡表,而 OopMap 确保这些修改在 GC 时被正确识别。

OopMap 与 GC 流程的协作​

以 ​​Young GC​​ 为例,流程如下:
1.​​触发 GC​​:新生代空间不足,需回收。
​​2.进入安全点​​:所有用户线程暂停,生成 OopMap。
3.​​枚举 GC Roots​​:
—根据 OopMap 遍历所有线程的栈帧和寄存器,收集指向新生代的引用(如局部变量 obj)。
—结合卡表,找到老年代中指向新生代的对象。
4.​​标记存活对象​​:从 GC Roots 出发,标记所有可达对象。
5.​​恢复线程​​:完成 GC 后,线程继续执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/79180.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数智化招标采购系统针对供应商管理解决方案(采购如何管控供应商)

随着《优化营商环境条例》深化实施&#xff0c;采购领域正通过政策驱动和技术赋能&#xff0c;全面构建供应商全生命周期管理体系&#xff0c;以规范化、数智化推动采购生态向透明、高效、智能方向持续升级。 郑州信源数智化招标采购系统研发商&#xff0c;通过供应商管理子系…

Fiori学习专题二十五:Remote OData Service

之前我们都是使用本地JSON来显示发票清单。这节课我们将调用一个UI5公共的OData Service 1.由于本地开发访问OData服务https://services.odata.org/V2/Northwind/Northwind.svc/会产生跨域问题&#xff0c;所以这里我们需要使用代理 新建一个终端&#xff1a;执行&#xff1a;n…

文件读取操作

如果需要从文件读入数据&#xff0c;并把输出数据保存为文件&#xff0c;需要使用文件读取。 freopen为file reopen&#xff0c;意为文件重新打开&#xff0c;实现重定向标准输入输出第一个参数为文件名可以修改&#xff0c;输入文件为.in&#xff0c;输出文件为.out第二个参数…

[Linux网络_68] 转发 | 路由(Hop by Hop) | IP的分片和组装

目录 1.再谈网络转发 2.路由 举个例子 3.分片和组装 IP 层 [Linux#67][IP] 报头详解 | 网络划分 | CIDR无类别 | DHCP动态分配 | NAT转发 | 路由器 1.再谈网络转发 我们在上一篇文章中知道了路由器的功能有&#xff1a; 转发DHCP | 组建局域网NAT 组建局域网功能表现&…

如何使用C语言手搓斐波那契数列?

斐波那契数列&#xff0c;第0项为0&#xff0c;第1项为1&#xff0c;第2项开始每项等于前两之和。&#xff08;有些题目从第一项开始&#xff0c;第一项为1&#xff0c;第二项也为1&#xff09;。 运行时&#xff0c;输入的n代表的是项数&#xff0c;而输出则代表的是该项的值。…

java: 警告: 源发行版 21 需要目标发行版 21

解决这个问题看三个地方的SDK版本信息是否正确&#xff1a; 1&#xff0c;打开cmd命令&#xff0c;输入 java -version ,查看版本是否正确&#xff1b; 2&#xff0c;打开模块设置&#xff08;F4&#xff09;&#xff0c;查看项目的SDK 3&#xff0c;查看模块的SDK

一区思路!挑战5天一篇NHANES预测模型 DAY1-5

挑战5天一篇预测模型NHANES Day1! 近期美国关闭seer数据库的信息在互联网上广泛传播&#xff0c;大家都在担心数据库挖掘是否还能做。这个问题其实是有答案的&#xff0c;数据库挖掘肯定能做&#xff0c;做没被关的数据库即可&#xff0c;同时留意一些国产数据库&#xff5e;…

centos7安装NVIDIA显卡

装备工作 我的系统版本 cat /etc/centos-releaseCentOS Linux release 7.9.2009 (Core) 内核版本 rpm -q kernel或者 rpm -qa|grep kernelkernel-3.10.0-1160.el7.x86_64 注意以上输出内核版本&#xff0c;按照我下面的操作步骤&#xff0c;不会出问题。否则重装系统都有可…

Web应用开发指南

一、引言 随着互联网的迅猛发展&#xff0c;Web应用已深度融入日常生活的各个方面。为满足用户对性能、交互与可维护性的日益增长的需求&#xff0c;开发者需要一整套高效、系统化的解决方案。在此背景下&#xff0c;前端框架应运而生。不同于仅提供UI组件的工具库&#xff0c…

Java @Transactional事物隔离级别和默认值详解

在 Java 开发中&#xff0c;Transactional 注解是 Spring 框架中用于管理事务的重要工具。它提供了多种配置选项&#xff0c;其中事务隔离级别是一个关键属性。本文将深入探讨 Transactional 注解的隔离级别默认值&#xff0c;并通过具体代码示例帮助你更好地理解和应用事务隔离…

车辆检测新突破:VFM-Det 如何用大模型提升识别精度

目录 ​编辑 一、摘要 二、引言 三、相关工作 四、Coovally AI模型训练与应用平台 五、方法 概述 综述&#xff1a;基于区域建议的检测 基于VehicleMAE的感知器 六、实验分析 数据集与评估指标 实现细节 属性预测模块预训练 与SOTA检测器的对比实验 消融实验 V…

微格式:为Web内容赋予语义的力量

一、什么是微格式? 微格式是一种建立在已有 Web 标准基础上的简单、开放的数据格式。它的核心思想是通过在 HTML 标签中添加特定的属性和类名,为网页内容添加语义注解,从而兼顾 HTML 文档的人机可读性。 简单来说,微格式就是一套约定俗成的 HTML 标记方式,让我们能够在不…

偏移成像中,原始地震采集数据的数据规则化(Data Regularization)

在油气地震资料处理中&#xff0c;柯希霍夫&#xff08;Kirchhoff&#xff09;积分法偏移成像对数据采集分布的均匀性较为敏感。当原始地震道数据存在空间分布不均匀时&#xff0c;会导致偏移噪声、假频或成像失真。数据规则化&#xff08;Data Regularization&#xff09;通过…

米壳AI:跨境电商图片翻译的“隐形革命”:当AI技术遇上全球化生意

一、行业观察&#xff1a;跨境卖家的“语言围城” 在亚马逊西班牙站&#xff0c;某家居品牌因产品图西班牙语翻译错误导致整批货物滞留港口&#xff1b;TikTok东南亚直播间里&#xff0c;美妆主播因马来语字幕错位引发消费者投诉……这些真实案例折射出跨境电商的集体困境&…

人工智能:如何将数据输入到神经网络中

文章目录 引言数据输入神经网络的重要性及示例以识别美女图片为例讲解数据输入不同应用的数据输入方式结语 人工智能是引领未来的前沿技术领域。通过这个系统性学习计划&#xff0c;我们将逐步深入如何将数据输入到神经网络中。无论你是初学者还是有一定基础的开发者&#xff0…

数据库12(游标)

游标语法 declare c1 cursor for select title from titles --定义一个游标c1&#xff0c;确定游标对应的列是titles表的title列&#xff0c;游标可以对应多个列 declare bname varchar(50) --声明变量 open c1 --初始化&#xff0c;开始使用游标 fetch next from c1 in…

第四部分:赋予网页健壮的灵魂 —— TypeScript(中)

目录 4 类与面向对象&#xff1a;构建复杂的组件4.1 类的定义与成员4.2 继承 (Inheritance)4.3 接口实现 (Implements)4.4 抽象类 (Abstract Class)4.5 静态成员 (Static Members) 5 更高级的类型&#xff1a;让类型系统更灵活5.1 联合类型 (|)5.2 交叉类型 (&)5.3 字面量类…

Vue3源码学习-提交限制

文章目录 前言✅ 1. ESLint 限制&#x1f527; 配置位置&#xff1a;✅ 启用了哪些规则&#xff08;核心&#xff09;&#xff1a;&#x1f4e6; 使用的插件和标准&#xff1a; ✅ 2. TSC 编译限制关键选项&#xff1a; ✅ 3. Git Hook 校验工具链配置例子&#xff08;package.…

Arthas 使用攻略

目录 背景 Arthas是什么&#xff1f; 安装 使用arthas-boot&#xff08;推荐&#xff09; 启动 常用命令 一键生成arthas命令的插件(强烈推荐) watch 一、命令语法结构 二、核心参数详解 三、实战场景 1. 基础观测 - 查看入参和返回值 2. 条件过滤 - 只关注特定参…

冥想类短视频批量剪辑自动混剪技术实践:从素材处理到智能合成全解析

一、引言&#xff1a;工业化内容生产的技术突围 在心理健康类内容爆发的当下&#xff0c;冥想类短视频凭借「低制作成本 高用户粘性」的特性成为热门赛道。本文结合实战经验&#xff0c;解析如何通过模块化素材处理、参数化合成引擎、自动化质量控制等技术手段&#xff0c;构…