当前位置: 首页 > news >正文

孤舟笔记 JVM篇三 JVM如何判断一个对象可以被回收?可达性分析比引用计数强在哪

文章目录

    • 一、先说结论:两种判定方案
    • 二、引用计数法:简单但有致命缺陷
    • 三、可达性分析:JVM 的选择
    • 四、GC Roots 有哪些?
    • 五、四种引用类型与回收
      • 强引用(Strong Reference)
      • 软引用(Soft Reference)
      • 弱引用(Weak Reference)
      • 虚引用(Phantom Reference)
    • 六、finalize():最后一次逃生机会
    • 对象回收判定 全景
    • 回答技巧与点评
        • 标准回答
        • 加分回答
        • 面试官点评

个人网站

面试官问"JVM 怎么判断对象可以回收",大部分人能说出"可达性分析",但追问"为什么不用引用计数"、“GC Roots 有哪些”、“四种引用类型和回收的关系”,就答不全了。

今天咱们把 JVM 判断对象存活的机制彻底讲透。

一、先说结论:两种判定方案

方案思路JVM 选择原因
引用计数每个对象记被引用次数,0 就回收无法解决循环引用
可达性分析从 GC Roots 出发,不可达就回收能解决循环引用

一句话记住:引用计数像"数有多少人认识你",可达性分析像"从领导查组织关系"——前者数人头可能数错(互相认识),后者查链路不会漏。

二、引用计数法:简单但有致命缺陷

原理:每个对象维护一个引用计数器,被引用 +1,引用断开 -1,计数为 0 则可回收。

Objecta=newObject();// 计数 = 1Objectb=newObject();// 计数 = 1a.field=b;// b 的计数 = 2b.field=a;// a 的计数 = 2a=null;// a 的计数 = 1(b 还引用着 a)b=null;// b 的计数 = 1(a 还引用着 b)// 计数永远不为 0 → 永远不会被回收!💥 但实际上已经无法访问了

循环引用导致内存泄漏——这是引用计数法的致命缺陷。

生活类比:引用计数像"互粉"——两个人互相关注,即使谁都不看对方的内容,粉丝数也不为零,系统不会清理。

Python 用引用计数但额外加了分代 GC 来处理循环引用,JVM 直接放弃了引用计数。

三、可达性分析:JVM 的选择

原理:从 GC Roots 出发,沿引用链向下搜索,不可达的对象就是可回收的。

GC Roots ├── objA → objB → objC ← 可达,存活 ✅ ├── objD → objE ← 可达,存活 ✅ └── objX ↗ objY ↘ objX ← 不可达,回收 ❌(循环引用但无 Root 连接)

关键:循环引用不再是问题——只要没有 GC Root 能到达这个环,整个环都是垃圾。

生活类比:可达性分析像"查组织关系"——从最高领导开始查,能查到的人就是在职的,查不到的就是离职的(不管他们之间是否互相认识)。

四、GC Roots 有哪些?

GC Root 类型示例
虚拟机栈中的引用方法中的局部变量、参数
静态变量类的 static 字段
常量字符串常量池的引用
本地方法栈中的引用JNI 中的引用
同步锁synchronized 持有的对象
JVM 内部引用基本数据类型的 Class 对象、常驻异常等

最常见的是前三种:栈帧中的局部变量、静态变量、常量。

publicclassGCRootsDemo{privatestaticObjectstaticVar;// GC Root:静态变量 👈privatestaticfinalStringCONSTANT="hello";// GC Root:常量 👈publicvoidmethod(){ObjectlocalVar=newObject();// GC Root:栈帧局部变量 👈synchronized(localVar){// GC Root:锁持有的对象// ...}}// 方法返回后,localVar 不再是 GC Root}

注意:GC Root 是"时刻在变"的——方法返回后,栈帧弹出,局部变量不再是 Root。

五、四种引用类型与回收

强引用(Strong Reference)

Objectobj=newObject();// 强引用 👈

只要强引用存在,永远不会被回收。即使 OOM 也不回收强引用对象。

软引用(Soft Reference)

SoftReference<byte[]>ref=newSoftReference<>(newbyte[1024*1024]);

内存不足时才会回收。适合做缓存。

// 缓存使用示例SoftReference<Image>cache=newSoftReference<>(loadImage());Imageimg=cache.get();// 可能返回 null(已被 GC 回收)if(img==null){img=loadImage();// 重新加载cache=newSoftReference<>(img);}

弱引用(Weak Reference)

WeakReference<Object>ref=newWeakReference<>(newObject());

下一次 GC 就会回收,不管内存是否充足。适合做临时缓存。

ThreadLocalMap 的 key 就是弱引用——这就是 ThreadLocal 可能泄漏的根源。

虚引用(Phantom Reference)

PhantomReference<Object>ref=newPhantomReference<>(newObject(),queue);

不影响对象生命周期,唯一作用是在对象被回收时收到通知。必须配合 ReferenceQueue 使用。

引用类型回收时机用途
强引用永远不回收(除非引用断开)日常使用
软引用内存不足时缓存
弱引用下一次 GC临时缓存、ThreadLocal
虚引用随时可回收跟踪 GC、管理堆外内存

六、finalize():最后一次逃生机会

对象被判定不可达后,还有一次自救机会:

1. 可达性分析发现对象不可达 2. 判断是否有必要执行 finalize() ├── 没有重写 finalize() 或已调用过 → 直接回收 └── 重写了且未调用过 → 放入 F-Queue 3. Finalizer 线程执行 finalize() 4. 对象在 finalize() 中重新与 GC Root 建立引用 → 逃生成功 👈 5. 未建立引用 → 下次 GC 回收

但 finalize() 不推荐使用:执行时间不确定、可能导致对象复活、性能开销大。用 try-finally 替代。

对象回收判定 全景

对象回收判定 全景 两种方案 ├── 引用计数 ── 简单但循环引用致命 └── 可达性分析 ── JVM 选择,从 GC Roots 出发 GC Roots ├── 虚拟机栈中的引用 ├── 静态变量 ├── 常量池引用 ├── 本地方法栈引用 ├── 同步锁 └── JVM 内部引用 四种引用 ├── 强引用 ── 永不回收 ├── 软引用 ── 内存不足时回收 ├── 弱引用 ── GC 即回收 └── 虚引用 ── 仅跟踪回收通知 对象回收流程 ├── 可达性分析 → 不可达 ├── 判断 finalize() │ ├── 无需执行 → 回收 │ └── 需执行 → F-Queue ├── finalize() 自救 → 下一轮再判 └── 未自救 → 回收 口诀:引用计数有循环,可达分析是正道, GC Roots 出发点,四类引用各不同, 强不回收软看内存,弱遇 GC 虚通知, finalize 别依赖,try-finally 才靠谱。

回答技巧与点评

标准回答

JVM 使用可达性分析判断对象是否可以回收。从 GC Roots 出发,沿引用链搜索,不可达的对象即为可回收对象。GC Roots 包括虚拟机栈中的引用、静态变量、常量、本地方法栈引用、同步锁等。JVM 不使用引用计数法是因为它无法解决循环引用问题。Java 的引用分为强引用(永不回收)、软引用(内存不足时回收)、弱引用(GC 即回收)和虚引用(仅跟踪回收通知)四种,不同引用类型影响对象的回收时机。

加分回答
  1. MAT 和 jmap 分析 GC Roots:线上排查内存泄漏时,用 jmap -histo 或 MAT(Memory Analyzer Tool)dump 堆内存,可以找到"到 GC Roots 的最短路径",定位哪个引用阻止了对象被回收。这是可达性分析在工具层面的实战应用
  2. Cleaner 和虚引用:Java 9 引入了 Cleaner 类替代 finalize(),内部用虚引用 + ReferenceQueue 实现。当对象被回收时,虚引用进入队列,Cleaner 的清理线程执行清理逻辑。这是管理堆外内存(DirectByteBuffer)的标准方式——比 finalize() 更安全、更可控
  3. 三色标记法:可达性分析的具体实现是三色标记——白色(未访问)、灰色(已访问但引用未处理完)、黑色(已访问且引用处理完)。最终白色的对象就是垃圾。CMS 和 G1 的并发标记都基于三色标记,用写屏障解决"漏标"问题
面试官点评

这道题考的是你对垃圾回收理论基础的理解。能说出"可达性分析、GC Roots"是基本要求,能讲清楚为什么不用引用计数、四种引用类型的区别,才算及格。如果你能提到 MAT 工具的使用、Cleaner 替代 finalize、三色标记法的实现,面试官会认为你对 GC 的理解不只在判定层面,还延伸到了工具和算法实现。

原文阅读


内容有帮助?点赞、收藏、关注三连!评论区等你 💪

http://www.jsqmd.com/news/783920/

相关文章:

  • CANN/pyasc数据连接API文档
  • 低空经济工业互联网中的数字孪生与智能体:IOC与平台协同的演进逻辑
  • ARM系统控制与调试接口:PPU与DAP详解
  • 有限单边响应游戏中的蒙特卡洛反事实遗憾最小化
  • 别再死记硬背API了!图解 LVGL 的“类”(lv_obj_class_t)与“对象”(lv_obj_t)继承体系
  • 别急着重启!Redis突然连不上的5分钟排查手册(附CentOS 7实战命令)
  • 宁波双利再生资源:镇海废旧金属回收推荐几家公司 - LYL仔仔
  • 抖音下载器终极指南:从零开始掌握批量下载与无水印提取
  • ChatGPT如何通过大学计算机安全课程考核?实验揭示AI对教育评估的冲击与机遇
  • 南京情绪障碍心理医院选择:专业机构服务解析 - 品牌排行榜
  • Facebook+Google+INS代运营公司优选,搭配海外市场AI推广平台与一站式出海营销服务商,赋能企业海外布局(附带联系方式) - 品牌2026
  • CANN-Bench直接启动算子示例
  • Godot文档仓库深度解析:从源码构建到高效使用的完整指南
  • 基于AI智能体的Wazuh自主安全运营流水线构建与实战
  • OpenClaw WebDAV插件:为开发工作空间开启跨平台文件访问
  • kafka 消费组内leader选举1 - 小镇
  • AI技能开发实践:将经典方法论转化为可执行的Agent技能
  • CANN/pypto: gcd函数文档
  • 无锡蔷薇动能科技:滨湖专业的高空车租赁公司电话 - LYL仔仔
  • 拆解ADAS域控成本密码:聊聊MCU端AutoSAR CP软件如何从DV、产测到量产一步步省钱
  • 新手入门指南使用 curl 命令快速测试 Taotoken 大模型接口
  • 【2026OD新机考】【排序】20260429-日志文件异常检测【Py/Java/C++/C/JS/Go六种语言OD真题】【欧弟算法】全网注释最详细分类最全的华子OD真题题解
  • 江浙沪非标搅拌罐定制推荐:本地厂家按需设计生产 - 品牌2025
  • 开源硬件设备统一管理平台:OpenClaw Mission Control 部署与实战
  • CANN/ops-nn HardSwish梯度V2算子
  • 别再乱改了!Discuz X3.5论坛模板安全修改与备份实操指南(附常见坑点)
  • 2026年太原手机号定向推广与GEO优化完全指南:5大品牌深度横评,精准获客成本下降50%的破局之道 - 优质企业观察收录
  • 智能天气API架构设计:从数据融合到开发者集成实战
  • Docker Compose部署Uptime Kuma与Mieru代理监控一体化方案
  • 高效节能压滤机厂家电话,2026去哪查询更省心 - 品牌2025