Java的GC(垃圾回收)中,标记无用对象的核心方法是可达性分析算法

发布时间:2026/10/3 8:25:17

Java的GC(垃圾回收)中,标记无用对象的核心方法是可达性分析算法 GC 干活的顺序永远是先找出垃圾、再收拾垃圾。找垃圾这一步HotSpot 用的是可达性分析Reachability Analysis先挑一组“绝对不能被回收”的对象当起点叫 GC Roots然后顺着引用关系一路找过去——能走到的对象算活着走不到的就当垃圾处理。这套思路现在是主流 JVM 的标配但大多数人只记得“可达性分析”这五个字。真被追问 GC Roots 具体指哪些、和引用计数法到底差在哪就说不利索了。而恰恰是这些细节在排查线上内存问题和面试里才真正用得上。一、可达性分析到底在做什么GC Roots 是哪些对象GC Roots 是“必须活着”的那批引用也是整张引用图的入口。规范里列了好几类落到代码上大致是这么些东西虚拟机栈里正在用的局部变量和参数。方法里写了Object obj new Object()只要这个方法还没返回obj指向的对象就不会被回收本地方法栈里 JNI 引用的对象。就是那些 C/C 写的 native 方法拿着的 Java 对象方法区里的静态属性。static Object cache new Object()只要这个类没被卸载它指向的对象就一直算活着方法区里的常量引用。比如字符串常量池里的字面量被 synchronized 锁住的对象。锁没释放作为锁对象的那个对象就不能回收JVM 内部自己用的引用。像系统类加载器、基本类型对应的 Class 对象int.class这种、常驻的异常对象比如 NPE 那个单例。这些 roots 有个共同点要么压根不在堆里要么虽然指向堆但 JVM 会强制它们保持存活。从这几个入口出发遍历堆里所有还活着的对象都能被覆盖到——这就是它判断得准的原因。一次完整的分析过程分析本身是一次图遍历但工程上有几个细节值得说枚举 GC Roots。这一步得暂停所有用户线程STWStop The World。不暂停不行你这边刚把 roots 数完那边用户线程就改了引用图都变了分析结果自然不作数。从 roots 出发遍历引用链。顺着引用关系递归地走走过的对象打上“可达”标记。遍历完还没打上标记的就是不可达、可回收的对象。二次标记。被判不可达的对象不会立刻回收中间还夹着一道 finalize() 的流程。这块是历史包袱下面单独拎出来说。finalize()该翻篇的历史一个对象被判不可达后如果它重写了 finalize() 并且从没执行过JVM 会把它丢进 F-Queue交给一条低优先级的 Finalizer 线程去跑 finalize()。跑的时候对象还有最后一次“自救”机会——比如在 finalize() 里把 this 赋给某个静态变量就重新接回了引用链这次就不回收了。只有自救失败才会真正被标记回收。这套机制现在基本可以当它不存在执行时机不确定、可能根本不执行还拖慢回收。从 Java 9 开始 finalize() 就被标成 deprecated官方三令五申让别用替代方案是 try-with-resources 或者 Cleaner。所以如果面试里还有题在问“finalize() 怎么让对象自救”答完最好补一句“新版本已经淘汰了这个机制”能听出你是跟着版本在走的。二、为什么 Java 没选引用计数法讲可达性分析绕不开要和引用计数法做个对比——毕竟后者才是最符合直觉的方案。思路很简单给每个对象挂一个计数器谁引用它就给计数加一引用失效就减一减到零说明没人用了直接回收。即时、简单、不用 STWPython 至今还在用它。但它有个治不好的病处理不了循环引用。A 引用 B、B 引用 A外部再没有别的引用了这俩本该一起被回收可它们互相把对方的计数顶在 1谁也归不了零就这么永远赖在内存里。要想补上这个窟窿就得额外做环检测或者配合别的算法复杂度一上去“实现简单”这个最大的优点也就没了。可达性分析从根上绕开了这个问题它不关心“谁引用了我”只关心“从根能不能走到我”。两个对象就算互相引用得再紧只要从 GC Roots 出发到不了它们一律算垃圾。三、JDK 21 之后GC 这两年挪到哪了“标记”这一步也就是可达性分析说实话十几年没什么大变化。但 GC 的另一半——“怎么把垃圾收得又快又省”——这两年动作特别大。既然都聊到 GC 了顺手把最近的动态捋一遍都是选型和面试容易碰到的。分代 ZGC 登场JDK 21JEP 439。ZGC 从 JDK 15 起就是正式功能卖点是亚毫秒级停顿、停顿时间几乎不随堆大小增长。但它早期不分代每次都扫全堆CPU 花在大量“注定还活着”的老对象上有点浪费。JDK 21 给它补上了分代堆分成年轻代和老年代年轻代回收得更勤绝大多数对象都是朝生夕死老年代扫得少。不过当时还得手动加-XX:ZGenerational才开。分代 ZGC 成为默认JDK 23JEP 474JDK 24 移除非分代模式JEP 490。接下来的两个版本把这个方向走到底了非分代 ZGC 被删除-XX:UseZGC开出来的就是分代。所以“用 ZGC 要记得加参数”这条老经验现在可以扔了。紧凑对象头JDK 25JEP 519。这是个不起眼但很实在的改动。堆里每个对象都带一个对象头存类指针、identity hash 之类的元数据64 位 JVM 默认开启指针压缩是 12 字节关掉压缩则是 16 字节。JEP 519 把它压到 8 字节业务代码一行不用改。对象越密集省得越多官方口径大约是堆占用降两成、GC 频率跟着降一成多。小对象特别多的应用大量短命的 DTO、事件对象收益最明显。到 JDK 27这个特性已经默认开启。分代 Shenandoah 转正JDK 25JEP 521。Shenandoah 这个低停顿收集器分代版本在 JDK 24 还是实验特性JDK 25 正式转正。它和 ZGC 定位相近但内存开销通常更省一点是低延迟场景下 ZGC 之外的另一条路。G1 减少同步JDK 26JEP 522。G1 至今仍是大多数服务端的默认收集器。JDK 26 动了它的写屏障减少 GC 线程和应用线程之间的同步竞争同样配置下吞吐能好一些。同一版本里AOT 缓存JEP 516也不再是 G1 的专利ZGC 也能用上启动更快。G1 变成所有环境的默认 GCJDK 27JEP 523。JDK 27 在 2026 年 9 月发布最有意思的一条是G1 取代了 Serial成为所有环境包括单核、内存受限的配置的默认收集器。这在几年前几乎是不可想象的——那会儿小内存环境默认还是 Serial。能让 G1 在受限环境下持平 Serial 的吞吐靠的正是 JEP 522 那类减少同步的优化。再往远看对象本身的形态可能要变。Project Valhalla 的值类型如果落地值对象不再有独立的对象头和引用身份能被压平存储。堆里对象更少、头也更省GC 的压力会跟着降——这比紧凑对象头更彻底。启动时间继续被压缩。Project Leyden 和 AOT 系列在把“预热”这件事往前挪以后 JVM 启动即接近峰值状态GC 也得跟着适配。低延迟正在变成默认预期。停顿从秒级走到毫秒级、再到亚毫秒级现在已经很少有人还为了几百毫秒的 GC 停顿去改架构了。关注点慢慢从“停顿”挪到了“内存占用”和“云上成本”。云原生和大堆场景。容器里对内存的感知、CPU 配额和 GC 线程数的关系会越来越重要。GC 是不是“感知容器 limit”直接决定它会不会被 OOMKilled。一句话判断“谁是垃圾”的办法早就不变了怎么把垃圾收得又快又省才是这两年在卷的地方。四、总结回到开头那个问题——Java 怎么标记无用对象答案是可达性分析从一组 GC Roots 出发遍历引用链能到达的算存活到不了的判定为可回收。它靠的是可达性而不是引用次数所以天然免疫循环引用这也是 Java 弃用引用计数法的根本原因。有几条值得带走判断对象可回收中间还有一次 finalize() 的“自救”机会但这套机制从 Java 9 起就被淘汰了别再用枚举 GC Roots 需要 STW这是所有基于可达性分析的收集器都无法完全绕开的停顿来源之一“标记”这一步很稳定“回收”这一步进化很快——从 G1 到分代 ZGC再到紧凑对象头都是在让回收更快、更省、停顿更短。说到底可达性分析只回答了“谁该死”真正影响线上表现的是收集器怎么高效地执行这个判决。拎清这条分界线很多 GC 问题和参数就不再是玄学了。
延伸阅读

更多相关文章

2026/10/3 9:15:22

企业级大模型落地:数据质量与治理才是规模化的胜负手

这两年聊企业级AI落地,几乎绕不开大模型。但真正在企业里趟过一遍的朋友,心里都有一本账:大模型选型、微调、部署、Agent编排,表面看的全是算法和算力,最后拼的却是数据。训练数据、微调数据、知识库数据、用户反馈数据…

2026/10/3 9:15:22

基于动态贝叶斯网络的配电网在线故障概率诊断系统

简介:本资源是一套基于贝叶斯概率推理的电力系统故障诊断仿真与实现方案,面向电气工程、智能电网方向的本科生、研究生及一线继保/自动化工程师,聚焦解决复杂电力系统中不确定性故障的识别、定位与决策支持问题。压缩包共32个文件&#xff08…

2026/10/3 9:15:22

LangGraph+FastAPI+Streamlit:从Demo到生产级AI Agent的完整改造指南

最近大半年,我花了大量时间把一个基于大模型的项目,从 Jupyter Notebook 里的演示 Demo,一步步改造成能够真正持续对外提供服务、接受真实用户请求的生产级 AI 助手。这个过程中最核心的技术组合,就是LangGraph FastAPI Streaml…

2026/10/3 9:15:22

时间复杂度深度解析:从大O复杂度到经典算法题优化实战

做了这么多年技术面试,我特别喜欢问同一个看似基础的问题:这段代码的时间复杂度是多少?大多数候选人能写出功能完整的程序,却说不清楚自己的代码为什么慢,更别提在大数据量下怎么选方案了。时间复杂度的概念谁都懂个大…

2026/10/3 9:15:22

RV1106 ISP与MIPI/LVDS配置实战:设备树调优与画质问题定位

做 RV1106 方案的第一个晚上,我盯着排线陷入沉思:sensor 供电正常、复位也拉完了,dmesg里死活不报 sensor 挂载,MIPI 时钟测出来却又是波形。后来翻了一整晚的资料,猜了无数种可能,最后发现根因既不在硬件上…

2026/10/3 9:10:21

语法分析器.cpp全解析:从Token流到AST与虚拟机指令生成

简介:编译原理课程中语法分析器环节的完整C实现代码,面向计算机专业学生与需要动手构建词法/语法分析模块的开发者。资源包仅含1个cpp文件,压缩后体积2KB,结构精简,便于直接阅读算法主流程,也适合作为课程实…

2026/10/2 8:16:46

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/10/2 18:20:53

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 10:48:55

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/3 0:04:31

国内大学生必备的AI写作辅助软件是哪款?

国内高校学生在论文写作过程中,越来越依赖AI辅助工具提升效率,主流方案以本土化全流程工具为核心,结合通用大模型与专业插件,覆盖选题构思、框架搭建、初稿撰写、查重降重、格式调整等关键环节,本文将深入解析当前主流…

2026/10/3 0:04:31

Codex接入Jev模型完整指南:配置方法、本地部署与踩坑排查

最近不少人在讨论 Codex 搭配 Jev 这套玩法,我一开始没太当回事,直到自己把 Jev 接进 Codex跑了几轮编码任务之后,才明白那些说“直接起飞”的人是怎么想的。Codex 作为工具本身已经够能打了,但模型固定、上下文策略固定&#xff…

2026/10/3 0:04:31

GitHub 热门: NVIDIA/Model-Optimizer

👋 Hi,我擅长 AI 大模型应用落地、意识解码与 AI 开发工具链 。 💡 创业路上,用技术换时间,一起把 AI 变成生产力 🚀 >GitHub 热门: NVIDIA/Model-Optimizer 凌晨两点,你刚把跑通了的 Qwen3.…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑