展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > 如何判断对象能否被回收?引用计数与可达性分析详解

如何判断对象能否被回收?引用计数与可达性分析详解

判断一个对象是否可回收,核心在于其能否被程序的活跃部分引用。若对象无法从GCRoots触达且无强引用,则被视为垃圾。主要依赖引用计数法和可达性分析法。引用计数法因循环引用问题易导致内存泄漏,如A引用B且B引用A时,计数永不归零,对象无法回收。现代JVM多采用可达性分析法,从GCRoots(如栈变量、静态属性、常量、JNI引用、活跃线程)出发遍历对象图,不可达对象被回收。为避免STW,现代GC采用并发标记,结合增量更新或SATB策略处理并发修改,辅以读屏障等技术,实现低延迟回收。

本文目录

    判断一个对象是否可回收,核心在于其能否被程序的活跃部分引用。若对象无法从GC Roots触达且无强引用,则被视为垃圾。主要依赖引用计数法和可达性分析法。引用计数法因循环引用问题易导致内存泄漏,如A引用B且B引用A时,计数永不归零,对象无法回收。现代JVM多采用可达性分析法,从GC Roots(如栈变量、静态属性、常量、JNI引用、活跃线程)出发遍历对象图,不可达对象被回收。为避免STW,现代GC采用并发标记,结合增量更新或SATB策略处理并发修改,辅以读屏障等技术,实现低延迟回收。

    如何判断一个对象是否可以被回收?(引用计数法、可达性分析法)

    一个对象是否可以被回收,核心判断依据在于它是否仍然被程序中的“活跃”部分所引用。简单来说,如果一个对象不再有任何强引用指向它,并且从程序的根节点(GC Roots)开始遍历时无法触达,那么它就可以被垃圾回收器视为“垃圾”,等待被清理。这主要依赖于两种策略:引用计数法和可达性分析法。

    解决方案

    在我看来,判断一个对象是否该“寿终正寝”了,这背后其实是内存管理机制的一场博弈。我们程序运行时产生的各种数据,总得有个地方放,也总得有个机制来决定什么时候能把它们腾出来,给新的数据用。引用计数法和可达性分析法,就是这场博弈中两种截然不同的裁判规则。

    引用计数法是一种相对直观的策略。它的基本思想是,给每个对象维护一个引用计数器。每当有一个地方引用它,计数器就加一;当引用失效时,计数器就减一。一旦计数器归零,这个对象就意味着没有其他对象再“关心”它了,自然就可以被回收了。这种方式的好处是,回收时机非常明确,几乎是实时的,一旦引用归零,对象就可以被立即回收,省去了等待垃圾回收器统一调度的时间。比如,像Python这样的语言,就部分采用了引用计数,但它也清楚地认识到这种方法的局限性,所以还辅以其他机制来弥补。

    然而,仅仅依赖引用计数,在实际的复杂场景中是远远不够的。它的最大痛点在于无法解决“循环引用”的问题。想象一下,对象A引用了对象B,同时对象B又引用了对象A。如果除此之外,没有其他任何外部引用指向A或B,那么它们的引用计数永远不会降为零。即便它们已经对程序没有任何用处了,它们也会像一对互相依偎的僵尸,永远霸占着内存空间,造成内存泄漏。这就是为什么现代主流的虚拟机,比如Java的JVM,更倾向于采用另一种更强大的判断方式——可达性分析法。

    可达性分析法,听起来可能有点抽象,但它的逻辑其实很像我们平时找东西。它会从一系列被称为“GC Roots”的根对象开始,沿着这些根对象所引用的路径,一步步地去遍历所有能够被触达到的对象。所有在遍历过程中能够被“标记”到的对象,都说明它们仍然是“活的”,程序还在使用它们。而那些从任何GC Roots出发都无法到达的对象,就自然而然地被判定为是不可用的,也就是可以被回收的垃圾。这种方法巧妙地避开了循环引用的难题,因为即使A和B互相引用,只要它们都无法从GC Roots被触达,它们最终都会被回收。这就像一张巨大的蜘蛛网,GC Roots就是网的固定点,所有能顺着网线爬到的地方都是安全的,爬不到的地方就等着被清理掉。

    为什么引用计数法无法彻底解决内存泄漏问题?

    引用计数法之所以无法彻底解决内存泄漏,其根本原因就在于它在处理循环引用时的无力。我们可以这样设想一个场景:你创建了两个对象,objectA 和 objectB。objectA 内部有一个字段指向 objectB,而 objectB 内部也有一个字段指向 objectA。现在,假设除了这两个对象彼此之间的引用之外,程序中再也没有其他任何地方引用 objectA 或 objectB 了。

    从我们人类的逻辑来看,这两个对象已经没有任何实际用处了,它们应该被回收。但是,如果使用引用计数法,objectA 的引用计数会因为 objectB 的引用而保持为1,objectB 的引用计数也会因为 objectA 的引用而保持为1。它们各自的计数器永远不会归零,因此垃圾回收器就永远不会认为它们是可回收的对象,即便它们已经“死”了。它们会一直占据着内存,直到程序结束,这就是典型的内存泄漏。

    这种问题在复杂的软件系统中非常常见,尤其是在对象之间存在复杂依赖关系时。像Python这样的语言,虽然主要使用引用计数,但为了解决这个问题,它会额外引入一个周期检测器(cycle detector)来专门查找并打破循环引用,这无疑增加了系统的复杂性。所以,在我看来,引用计数法虽然直观且易于实现,但在面对现代软件的复杂对象图时,它的局限性就暴露无遗了。

    现代JVM中,哪些对象会被视为GC Roots?

    在Java的JVM中,GC Roots是可达性分析算法的起点,它们是那些被认为“不可回收”的、能够被程序直接访问到的引用。理解GC Roots非常关键,因为它决定了哪些对象是“活”的。通常,以下几类对象会被JVM视为GC Roots:

    • 虚拟机栈(栈帧中的本地变量表)中引用的对象: 当一个方法被调用时,JVM会为它创建一个栈帧,其中包含本地变量表。本地变量表里存放着各种基本类型数据和对象引用。如果一个对象被本地变量表中的引用所指向,那么这个对象就是GC Root。举个例子,你在一个方法里声明了一个 Object obj = new Object();,那么 obj 这个引用就指向了栈上的一个对象,这个对象就是GC Root。
    • 方法区中类静态属性引用的对象: 在Java中,类的静态变量(static 字段)是存储在方法区(在JDK 8及以后,通常是元空间的一部分)的。如果一个静态变量引用了一个对象,那么这个对象也会被视为GC Root。这是因为静态变量的生命周期与类本身相同,只要类还存在,这个静态引用就一直存在。
    • 方法区中常量引用的对象: 比如字符串常量池中的对象,或者通过 final 关键字修饰的常量,它们被方法区中的常量表引用。这些对象也是GC Roots。
    • 本地方法栈中JNI(Native方法)引用的对象: 当Java程序调用本地方法(Native Method,例如C或C++代码)时,本地方法栈会为这些方法服务。在本地方法中,可能会创建或引用Java对象。这些被本地方法引用的对象,也必须被视为GC Roots,以防止在本地代码还在使用它们时被垃圾回收。
    • 所有活跃线程本身: 线程对象本身也是GC Roots。因为线程是程序执行的最小单位,它的存在意味着程序还在运行,它所持有的引用自然是“活”的。

    这些GC Roots就像是内存中的“锚点”,它们是垃圾回收器进行可达性分析的起点。所有能够通过这些锚点被直接或间接访问到的对象,都将被标记为“存活”对象,不会被回收。反之,那些无法从任何GC Root追溯到的对象,才会被判定为可回收。

    可达性分析法在实际应用中是如何避免“暂停一切”(Stop-The-World)的?

    “Stop-The-World”(STW)是垃圾回收领域一个让人头疼的词。在早期的可达性分析实现中,为了保证对象图的准确性,垃圾回收器在执行标记阶段时,需要暂停所有用户线程(mutator),直到标记工作完成。这会导致应用程序出现明显的卡顿,对于追求低延迟和高并发的应用来说是难以接受的。为了解决这个问题,现代的JVM垃圾回收器引入了许多高级技术来减少或避免STW,主要思路是让标记过程与用户线程并发执行。

    其中最核心的技术之一是并发标记(Concurrent Marking)。它允许垃圾回收器在用户线程运行的同时进行对象的标记。但并发标记会带来一个新的挑战:在GC标记过程中,用户线程可能会修改对象引用关系,导致已经标记的对象变得不可达,或者未标记的对象变得可达。这被称为“漏标”和“错标”问题。

    为了解决这些问题,现代垃圾回收器通常会采用以下两种策略:

    1. 增量更新(Incremental Update):这种策略认为,只要一个对象在标记阶段被用户线程修改了引用关系,使得它从一个已标记对象指向了一个未标记对象(即“新”引用指向“旧”未标记对象),那么这个“旧”的未标记对象就应该被重新标记。它的核心思想是,当一个引用从已标记对象指向未标记对象时,就把这个引用记录下来,在下一次GC周期或者特定的阶段重新扫描这些记录的引用。这就像给那些可能被“遗漏”的对象打上一个补丁,确保它们不会被错误回收。
    2. 原始快照(Snapshot-At-The-Beginning, SATB):SATB策略则更激进一些。它假设在GC开始标记的那一刻,所有存活的对象都已经被“拍了一张快照”。如果在GC标记过程中,用户线程删除了一个从已标记对象到未标记对象的引用(即“旧”引用被删除),那么这个被删除的引用所指向的对象,即使在快照后变得不可达,也仍然会被认为是存活的。换句话说,它只关心对象在GC开始时的可达性,对于后续的引用删除操作不敏感。这可以避免漏标问题,但可能会导致一些“浮动垃圾”(即本可以回收但因为SATB而保留到下一轮GC的对象)。

    例如,G1垃圾回收器就采用了SATB来处理并发标记阶段的并发问题。ZGC和Shenandoah等更先进的垃圾回收器则在此基础上,结合了读屏障(Read Barrier)等技术,进一步减少甚至几乎消除了STW时间,它们通过在对象被读取时进行额外的操作来维护对象图的准确性,使得GC暂停时间可以控制在非常低的毫秒级别,甚至亚毫秒级别,从而实现对应用程序几乎无感知的垃圾回收。这些技术的引入,使得JVM在保证内存安全的同时,也能满足现代应用对高性能和低延迟的严苛要求。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    精品专题 更多
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    Mac软件 更多
    Aperty macOS版
    Aperty macOS版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw macOS版
    AionClaw macOS版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy macOS版
    Loomy macOS版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirBuddy 3 macOS版
    AirBuddy 3 macOS版

    AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

    AirServer macOS版
    AirServer macOS版

    AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

    AnyDesk macOS版
    AnyDesk macOS版

    AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

    WINDOWS 更多
    讯飞绘文 网页版
    讯飞绘文 网页版

    讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

    Maya(Autodesk Maya) Windows版
    Maya(Autodesk Maya) Windows版

    Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

    Aperty Windows版
    Aperty Windows版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw Windows版
    AionClaw Windows版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy Windows版
    Loomy Windows版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirServer Windows版
    AirServer Windows版

    AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。