跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Javajava算法

JVM 垃圾回收机制:可达性分析算法详解

综述由AI生成详细解析了 JVM 垃圾回收中的可达性分析算法。首先介绍了 GC Roots 作为标记起点的原理,以及对象如何通过引用链与 GC Roots 连接。接着阐述了 finalize 方法的作用时机、次数限制及其被弃用的原因,指出其在资源清理上的风险。重点讲解了 JVM 针对重写 finalize 方法的对象进行的“两次标记”机制:第一次标记筛选出需执行 finalize 的对象并放入 F-Queue,若对象在 finalize 中实现“自救”(重新关联引用),则在第二次标记中被保留,否则被回收。最后通过比喻形象说明了各组件的关系。

城市逃兵发布于 2026/3/22更新于 2026/5/78.4K 浏览
JVM 垃圾回收机制:可达性分析算法详解

JVM 垃圾回收机制:可达性分析算法详解

虽然方法区也有 GC,但条件更为苛刻(如所有实例被回收、ClassLoader 被回收等),本文主要讲解堆内存。

可达性分析算法

垃圾收集器通常采用可达性分析算法。其核心规则是:如果某个对象没有通过引用链连接到 GC Roots,则表明该对象不可达,会被判定为可回收对象。

GC Roots 是一个数据结构,包含多种引用类型。例如,在方法中 new 出来的对象会被强引用连接。

根据 JVM 内存区域的分配,new Student() 是在 Java 堆中开辟了一块空间用于存放对象数据。Java 中虽取消了显式指针,但变量本质上起到了指针的作用。如果将变量设置为 null,代表将引用指向的位置断开,此时 Java 堆中的对象就会被可达性算法检测为'GC Roots 到该对象不可达'。

finalize 方法

finalize 是所有类中都存在的一个方法,它只有在 GC 过程中会被触发使用,而且只会被使用一次,并且已被官方弃用。

书中提到:它并不能等同于 C 和 C++ 语言中的析构函数,而是 Java 刚诞生时为了使传统 C/C++ 程序员更容易接受 Java 所做出的一项妥协。它的运行代价高昂、不确定性大、无法保证各个对象的调用顺序,如今已被官方明确声明为不推荐使用的语法。

在 Java 源码中也如是说到:子类重写 finalize 方法以处置系统资源或执行其他清理。finalize 的一般约定是:如果 Java 虚拟机确定任何尚未死亡的线程都无法再访问此对象(没被 GC Roots 引用连接),则调用 finalize。

除非是由于其他已准备就绪的对象或类的 finalize 所采取的行动。finalize 方法可以采取任何行动,包括使此对象再次可用于其他线程;这句话的核心是解释 finalize 方法的一个关键特性:对象可以在自己的 finalize 方法中'复活'自己,或者复活其他对象。

然而,finalize 的通常目的是在对象被不可撤销地丢弃之前执行清理操作。例如,表示输入/输出连接的对象的 finalize 方法可能会在对象被永久丢弃之前执行显式 I/O 事务以断开连接。

Java 虚拟机对任何给定对象调用 finalize 方法的次数都不会超过一次。

使用 finalize 可能会导致安全性、性能和可靠性方面的问题。其中虚拟机并不承诺 finalize 的内容会被执行完成,如果一个对象的 finalize 方法执行非常缓慢,或者发生了死循环(例如 while(true)),将导致 F-Queue 队列一直堆积,最终可能导致整个内存回收子系统崩溃,甚至 OOM(内存溢出)。

两次标记

当满足以下两种需求时,才会触发两次标记(如果 finalize 没被重写,那么在第一次 GC 标记后就会直接给对象空间清除了):

  • Java 堆中的对象不被 GC Roots 引用连接
  • finalize 被重写

第一次标记

  • Step 1(可达性分析): 发现对象不可达。
  • Step 2(第一次标记/筛选): 判断该对象是否有必要执行 finalize() 方法。
    • 如果对象没有覆盖 finalize() 方法,或者 finalize() 已经被虚拟机调用过,那么虚拟机视为'没有必要执行'。—— 这种情况下,对象直接被判定为'死亡',等待回收,不会进入 F-Queue。
    • 只有'有必要执行'的对象,才会被放入 F-Queue。

接下来会由虚拟机自动建立的、低调度优先级的 Finalizer 线程去调用队列中对象的 finalize 方法,对象如果在 finalize 中将自己赋值给一个变量(即赋值给 GC Roots 中的一个对象)。

第二次标记

第二次标记所做的事情是看队列中的对象是否自救成功。

当对象自救后,垃圾处理器在进行二次标记处理时就会发现该对象还存在引用,则会取消对其的空间释放。

public class Zombie {
    static Zombie savedInstance;
    // 一个存活的静态引用
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Finalize called!");
        savedInstance = this; // 关键行动:将即将被销毁的当前对象赋给静态变量
                              // 对象现在'复活'了!
    }
    public static void main(String[] args) throws InterruptedException {
        Zombie z = new Zombie();
        z = null; // 删除唯一引用,z 变得不可达
                  // 建议 GC,第一次
        System.gc();
        Thread.sleep(1000);
        if (savedInstance != null) {
            System.out.println("Zombie object is alive!");
        } else {
            System.out.println("Zombie object is dead.");
        }
        // 再次尝试回收(注意:一个对象的 finalize 最多被 JVM 调用一次)
        savedInstance = null;
        System.gc();
        Thread.sleep(1000);
        System.out.println("Process finished.");
    }
}

从上面的两次标记过程我们不难发现,标记实际也可以被称为处理,但是只有当 finalize 被重写后的对象才存在两次标记。

比喻

想象这是一个超级大的,人人自危的修仙世界:

  • GC Roots - 各个宗门,它会根据你的作用来决定你是否是'宗门弟子'(GC Roots 引用)
  • Java 堆 - 存在洪荒野兽的修仙世界
  • 垃圾收集器 - 一个规则怪兽,某个时间中,他会杀死所有没被宗门庇护的修仙者
  • finalize 方法 - 修仙者的'保命符',可能是能让修仙者直接成为某个宗门的弟子,(但是只能使用一次)

目录

  1. JVM 垃圾回收机制:可达性分析算法详解
  2. 可达性分析算法
  3. finalize 方法
  4. 两次标记
  5. 第一次标记
  6. 第二次标记
  7. 比喻
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • GitHub 2026 年开源 AI 项目热度深度分析
  • HarmonyOS 6.0 OAID 服务正式支持 TV 设备
  • 基于 Copilot 反向代理的 Claude 本地部署指南
  • VR 科普学习机赋能新课堂:沉浸式教育应用
  • AI 大模型源码解析:打造 GitHub 智能答疑助手
  • OpenVLA 架构解析:基于 Prismatic VLM 与下一个 Token 预测的动作生成
  • OpenClaw 本地部署配置飞书机器人实战
  • OkHttp 网络请求库核心原理深度解析
  • 图片浏览与切图功能演示
  • Rust 与 WebAssembly 实战:在浏览器与 Node.js 中运行高性能代码
  • Harness Engineering 工程化教程:AI Agent 复杂长任务可靠执行
  • C++ 继承机制详解:从基础概念到多继承模型
  • TRAE SOLO 远程开发体验与 cpolar 内网穿透方案
  • Linux 下 NFS 服务端与客户端的安装配置及挂载指南
  • 本地项目上传 Gitee 完整指南:新手避坑版
  • Apache IoTDB 时序数据库深度解析与实战指南
  • Python 四大 Web 框架对比:FastAPI、Django、Flask 与 Tornado
  • 保姆级教程:从零搭建AI系统权限控制系统
  • Verilog 零基础入门:语法、仿真与 FPGA 实战
  • OpenClaw 完整搭建指南:从零打造 AI 助手

相关免费在线工具

  • 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