跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

JVM 垃圾回收入门:对象死亡判断的底层逻辑

综述由AI生成JVM 垃圾回收的核心在于准确判断对象是否死亡。主要机制包括引用计数法和可达性分析算法。后者通过 GC Roots 遍历引用链,解决了循环引用问题。不可达对象需经二次标记确认,finalize() 方法虽提供自救机会但存在风险。掌握这些原理有助于应对面试中的底层逻辑追问。

佛系玩家发布于 2026/3/28更新于 2026/6/1321 浏览
JVM 垃圾回收入门:对象死亡判断的底层逻辑

JVM 垃圾回收入门:对象死亡判断的底层逻辑

在 Java 开发中,JVM 垃圾回收机制不仅是面试的高频考点,更是理解内存管理的基石。很多人只背结论却不懂底层逻辑,一旦遇到追问就容易卡壳。我们直接从原理出发,拆解对象死亡的两种核心判断方式,顺便梳理几个高频追问,帮你把这块硬骨头啃下来。

为什么需要判断对象死亡?

JVM 的堆内存是有限的,如果创建的对象不再被使用,却一直占用内存,久而久之会导致内存泄漏,甚至触发 OOM(OutOfMemoryError)异常,导致程序崩溃。

因此,垃圾回收的核心前提就是:准确判断哪些对象已经'死亡'(不再被任何地方引用,失去使用价值),才能安全地回收其占用的内存,保证程序稳定运行。简单来说,判断对象死亡是垃圾回收的'前置操作',也是避免内存浪费的关键。

对象死亡判断的两大核心机制

JVM 判断对象死亡,主要依赖两种机制:引用计数法 和 可达性分析算法。其中,可达性分析算法是目前主流采用的核心方式,而引用计数法因自身缺陷被淘汰。

引用计数法(已淘汰)

原理

给每个对象分配一个'引用计数器'。当对象被引用一次,计数器值加 1;当引用失效(比如变量赋值为 null),计数器值减 1。当计数器值为 0 时,认为该对象已经死亡,可被回收。

示例
// 创建对象,计数器初始化为 1
Object obj = new Object();

// 新增引用,计数器变为 2
Object obj2 = obj;

// 引用失效,计数器变为 1
obj = null;

// 引用失效,计数器变为 0(对象可被回收)
obj2 = null;
致命缺陷:循环引用问题

引用计数法最大的问题是无法解决'循环引用'场景,这也是它被 JVM 淘汰的核心原因。

比如两个对象互相引用,但都不再被其他地方引用,此时它们的计数器值都为 1,无法被回收,导致内存泄漏:

class A {
    private B b;
    public void setB(B b) { this.b = b; }
}

class B {
    private A a;
    public void setA(A a) { this.a = a; }
}


    ();
    ();
a.setB(b);
b.setA(a);


a = ;
b = ;
// 循环引用
A
a
=
new
A
B
b
=
new
B
// 引用失效,但计数器都为 1,无法回收
null
null

正因为这个缺陷,目前主流 JVM(如 HotSpot)均未采用引用计数法,而是选择了可达性分析算法。

可达性分析算法(主流核心)

原理

以'GC Roots'(垃圾回收根节点)为起点,向下遍历所有引用链。如果一个对象没有任何一条引用链连接到 GC Roots,则认为该对象不可达,标记为'可回收对象'。

简单来说:GC Roots 是'不会被回收'的核心对象,从这些对象出发,能找到的对象都是'存活'的,找不到的就是'待回收'的。

GC Roots 的组成

GC Roots 必须是'绝对不会被垃圾回收'的对象,主要包括以下 4 类(面试常问):

  1. 虚拟机栈(栈帧中的局部变量表)中引用的对象;
  2. 方法区中类静态属性引用的对象;
  3. 方法区中常量引用的对象;
  4. 本地方法栈中 JNI(Native 方法)引用的对象。
可达性分析流程
  1. 确定 GC Roots 集合;
  2. 从 GC Roots 出发,遍历所有引用关系,形成'存活对象引用链';
  3. 未被引用链覆盖的对象,标记为'不可达对象',进入待回收队列。
对象死亡的最终确认(二次标记)

不可达对象并非立即死亡,还需经过'二次标记'才能确认:

  1. 第一次标记:通过可达性分析,标记为不可达对象;
  2. 筛选判断:检查该对象是否重写了 finalize() 方法,且该方法未被执行过。
    • 若未重写 finalize(),或已执行过,则直接标记为'死亡对象';
    • 若重写了且未执行,则将对象放入'F-Queue'队列,由虚拟机自动执行 finalize() 方法;
  3. 二次标记:执行完 finalize() 方法后,再次判断对象是否可达。若仍不可达,则正式标记为死亡对象;若重新变得可达(比如在 finalize() 中重新建立引用),则移除待回收队列,继续存活。

⚠️ 注意:finalize() 方法是对象'最后的自救机会',但实际开发中不建议使用(执行时机不确定、效率低,还可能导致内存泄漏),JDK9 已标记为过时方法。

面试官追问环节

这部分是面试加分项,提前应对面试官的连环追问,比纯记八股更有用。

追问 1:JVM 为什么不采用引用计数法,而是选择可达性分析算法?

答:核心原因是引用计数法无法解决'循环引用'问题,会导致内存泄漏;而可达性分析算法通过 GC Roots 为起点,能完美避开循环引用的坑(循环引用的对象无法连接到 GC Roots,会被标记为不可达)。此外,可达性分析算法的判断逻辑更严谨,能更准确地识别存活对象。

追问 2:GC Roots 具体包含哪些对象?有没有补充的?

答:核心是 4 类(虚拟机栈局部变量、方法区静态属性、方法区常量、本地方法栈 JNI 引用),补充 2 类特殊情况:

  • 被同步锁(synchronized)持有的对象;
  • JVM 内部的对象(比如类加载器、系统类、常驻内存的对象)。

追问 3:对象被标记为不可达后,为什么不立即回收?

答:为了给对象一次'自救'的机会,即通过 finalize() 方法重新建立引用,避免误回收(虽然实际开发中很少用,但 JVM 设计时保留了这个机制)。同时,批量回收不可达对象能提高垃圾回收的效率,避免频繁触发回收操作。

追问 4:finalize() 方法能真正让对象'自救'吗?为什么不推荐使用?

答:理论上可以(在 finalize() 中给对象重新赋值,建立到 GC Roots 的引用),但实际不推荐,原因有 3 点:

  1. 执行时机不确定:finalize() 由 JVM 的 Finalizer 线程执行,线程优先级低,无法保证及时执行;
  2. 效率低:Finalizer 线程执行速度慢,大量使用会拖慢垃圾回收效率;
  3. 易导致内存泄漏:若 finalize() 中出现异常,对象无法被回收,长期积累会导致内存泄漏。

总结

本文核心讲解了 JVM 判断对象死亡的 2 种机制,重点掌握可达性分析算法(GC Roots、引用链遍历、二次标记),这是面试的核心考点。

核心要点总结:

  1. 判断对象死亡是垃圾回收的前提,避免内存泄漏和 OOM;
  2. 引用计数法因循环引用缺陷被淘汰;
  3. 可达性分析算法是主流,核心是 GC Roots 和引用链遍历;
  4. 不可达对象需经过二次标记,finalize() 可自救但不推荐使用。

建议结合上述追问,反复梳理原理,面试时才能从容应答。

目录

  1. JVM 垃圾回收入门:对象死亡判断的底层逻辑
  2. 为什么需要判断对象死亡?
  3. 对象死亡判断的两大核心机制
  4. 引用计数法(已淘汰)
  5. 原理
  6. 示例
  7. 致命缺陷:循环引用问题
  8. 可达性分析算法(主流核心)
  9. 原理
  10. GC Roots 的组成
  11. 可达性分析流程
  12. 对象死亡的最终确认(二次标记)
  13. 面试官追问环节
  14. 追问 1:JVM 为什么不采用引用计数法,而是选择可达性分析算法?
  15. 追问 2:GC Roots 具体包含哪些对象?有没有补充的?
  16. 追问 3:对象被标记为不可达后,为什么不立即回收?
  17. 追问 4:finalize() 方法能真正让对象“自救”吗?为什么不推荐使用?
  18. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 飞算 JavaAI 本地化智能编程工具实战解析
  • Python 技能实战:从自动化办公到数据分析的职业进阶
  • Stack-Chan 机器人开发与使用指南
  • Python+ROS2 通用智能系统工业级通信框架
  • Java 集成高德开放平台 WebAPI 实现 POI 搜索
  • Kimi K2.5 开源部署、API 接入、Agent 集群与多模态实战
  • Kimi K2.5 开源部署、API 接入、Agent 集群与多模态视觉实战
  • Kafka 核心架构与分布式存储
  • RAG 技术原理、核心流程与最佳实践指南
  • AI 视频生成技术迈入实用阶段:解析央视短片背后的技术路径
  • Claude Scholar:AI 学术研究全流程助手
  • 前端请求后端 404/405/500 状态码排查与解决指南
  • Java Map 常用方法与实现类深度解析
  • Linux 进程间通信进阶:管道与共享内存详解
  • C/C++ 全局变量跨文件机制:链接属性与 static 关键字
  • AWS Kiro 账号池管理系统:OpenAI 兼容代理服务,支持多账号与 OIDC 认证
  • Python 第三方库实战与综合案例:程序员鼓励师与学生管理系统
  • AI 辅助自动生成 WPS Visio 流程图实战指南
  • Jimeng AI Studio 零代码 AI 绘画实战指南
  • OpenClaw 本地化部署及 QQ 机器人接入教程

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online