【JVM原理详解】23-四种引用类型-强软弱虚
23-四种引用类型强、软、弱、虚前两篇讨论的 GC Roots 和可达性分析本质是在回答对象是否被引用。但现实中我们常常希望对引用做更精细的控制比如缓存里的对象内存够用就保留、不够再回收又比如有些资源需要在对象被回收前执行一段清理逻辑。Java 提供了四种引用类型来满足这些需求——StrongReference、SoftReference、WeakReference、PhantomReference。本篇将逐一剖析它们的语义、实现与典型应用。引用强度的设计意图JDK 1.2 之前Java 只有引用这一种概念——只要引用存在对象就永不回收。这对缓存、临时映射等场景过于刚性。于是在 1.2 引入了java.lang.ref包将引用划分为四个级别从强到弱依次为强引用 (Strong) 软引用 (Soft) 弱引用 (Weak) 虚引用 (Phantom)引用越弱对象被回收的优先级越高。这套机制让开发者可以在对象可达性之外再叠加一层回收优先级的语义。StrongReference永不主动回收StrongReference 就是我们日常写的普通引用ObjectobjnewObject();// 强引用只要强引用还存在垃圾收集器永远不会回收被引用的对象——哪怕 OOM 也不会。这是 JVM 规范的硬性保证也是其他三种引用的对比基准。工程上绝大多数内存泄漏都源于本应是临时引用的强引用被意外长期持有比如publicclassLeakyCache{// 强引用持有永不回收privatestaticfinalMapString,byte[]CACHEnewHashMap();publicstaticvoidput(Stringkey,byte[]data){CACHE.put(key,data);}// 没有 remove 方法 → 内存泄漏}避免强引用泄漏的核心是显式置 null或用更弱的引用替代。SoftReference内存不足时回收SoftReference 描述还有用但非必需的对象。在 JVM 抛出 OOM 之前会清理软引用对象清理后仍内存不足才会真正 OOM。典型应用缓存importjava.lang.ref.SoftReference;importjava.util.HashMap;importjava.util.Map;/** * 基于软引用的简单缓存 * 适用 JDK 8/11/17 */publicclassSoftCacheK,V{privatefinalMapK,SoftReferenceVcachenewHashMap();publicvoidput(Kkey,Vvalue){cache.put(key,newSoftReference(value));}publicVget(Kkey){SoftReferenceVrefcache.get(key);returnrefnull?null:ref.get();}}SoftReference.get()在对象被回收后返回null调用方需要处理这种缓存未命中的情况。回收时机HotSpot 对软引用的回收遵循最近最少使用原则最近被访问过的软引用会保留更久。这是通过-XX:SoftRefLRUPolicyMSPerMB默认 1000ms参数控制的堆中每剩余 1MB 空间软引用的存活时间增加约 1 秒。# 调大软引用存活时长每 MB 多活 2 秒java-XX:SoftRefLRUPolicyMSPerMB2000-cpMyApp com.example.Main需要注意G1 之后默认开启-XX:UseG1GC软引用回收策略略有变化G1 通过-XX:G1SoftRefLRUPolicyMSPerMB单独控制。SoftReference 的代价软引用对象本身SoftReference实例是强引用它内部通过一个referent字段弱指向真正的对象。GC 在回收 referent 前会先检查软引用条件满足则把 referent 字段置 null、并把 SoftReference 实例放入关联的 ReferenceQueue。这意味着软引用本身仍占内存需要配合队列清理。WeakReference下次 GC 即回收WeakReference 比 SoftReference 更弱——只要发生 GC无论内存是否充足弱引用指向的对象都会被回收前提是没有其他强引用。典型应用WeakHashMapimportjava.util.WeakHashMap;classUserHolder{privatestaticfinalWeakHashMapObject,StringMETAnewWeakHashMap();publicstaticvoidbind(Objectkey,Stringtag){META.put(key,tag);}// 当 key 对象在外部失去引用后下次 GC 会自动清理该 entry}WeakHashMap的 key 是弱引用value 是强引用。这种key 弱、value 强的结构有一个潜在陷阱value 本身可能反向引用 key导致 key 实际上无法回收。这类问题排查难度较大建议 value 持有的对 key 的引用尽量用弱引用。ThreadLocal 的实现ThreadLocal内部也是用WeakReference持有 ThreadLocal 实例本身// ThreadLocal.ThreadLocalMap.Entry 源码片段staticclassEntryextendsWeakReferenceThreadLocal?{Objectvalue;Entry(ThreadLocal?k,Objectv){super(k);// key 是弱引用valuev;// value 是强引用}}这就解释了为什么 ThreadLocal 实例本身可以被回收但value 仍可能泄漏value 强引用了真实数据只要线程不死value 就不会被回收。所以使用 ThreadLocal 必须remove()。PhantomReference对象回收前通知PhantomReference 是最弱的一种通过它甚至无法获取到对象本身——get()永远返回null。它的唯一作用是在对象被回收时收到通知常用于资源清理。ReferenceQueue 工作原理四种引用都可以在创建时关联一个ReferenceQueue。GC 回收 referent 后会把对应的 Reference 对象入队enqueue。应用线程从队列中取出 Reference就能知道哪个对象被回收了。创建引用 GC 回收 referent Reference Queue ────────────────────── Reference 入队 | 应用线程 poll → 执行清理虚引用必须配合 ReferenceQueue 使用否则毫无意义——因为它的get()永远是 null没有队列就没法感知回收事件。Cleaner 机制JDK 9 引入了java.lang.ref.Cleaner它本质上是一个虚引用 ReferenceQueue 的封装替代了已过时的finalize()。importjava.lang.ref.Cleaner;/** * 使用 Cleaner 管理本地资源 * 适用 JDK 9 */publicclassNativeResourceimplementsAutoCloseable{privatestaticfinalCleanerCLEANERCleaner.create();privatestaticclassStateimplementsRunnable{privatelongnativeHandle;// 模拟本地句柄State(longhandle){this.nativeHandlehandle;}Overridepublicvoidrun(){// 对象被回收时回调if(nativeHandle!0){freeNative(nativeHandle);nativeHandle0;}}}privatefinalStatestate;privatefinalCleaner.Cleanablecleanable;publicNativeResource(longhandle){this.statenewState(handle);this.cleanableCLEANER.register(this,state);}Overridepublicvoidclose(){cleanable.clean();// 主动清理}privatestaticnativevoidfreeNative(longhandle);}关键点State不持有外部对象NativeResource的引用避免阻止其回收。Cleaner.register(this, state)内部把this用虚引用包装、关联到 Cleaner 自己的队列。NativeResource被回收时Cleaner 线程会执行state.run()完成清理。也可以主动调用cleanable.clean()提前清理幂等。PhantomReference vs finalizefinalize()在 JDK 9 被标记 deprecatedJDK 18 起默认禁用。它的核心问题是可以复活对象、执行时机不确定、执行线程无保证。Cleaner 通过独立的清理线程和不可访问 referent的设计规避了这些问题。完整对比示例下面用一段代码同时演示四种引用在 GC 前后的行为差异importjava.lang.ref.*;importjava.util.ArrayList;importjava.util.List;publicclassReferenceDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{ReferenceQueueObjectqueuenewReferenceQueue();// 1. 强引用ObjectstrongnewObject();// 2. 软引用SoftReferenceObjectsoftnewSoftReference(newObject(),queue);// 3. 弱引用WeakReferenceObjectweaknewWeakReference(newObject(),queue);// 4. 虚引用PhantomReferenceObjectphantomnewPhantomReference(newObject(),queue);// 检查引用是否还在System.out.println(Before GC:);System.out.println( strong (strong!null));System.out.println( soft (soft.get()!null));System.out.println( weak (weak.get()!null));System.out.println( phantom (phantom.get()!null));// 总是 null// 触发 GCSystem.gc();Thread.sleep(500);System.out.println(After GC:);System.out.println( strong (strong!null));System.out.println( soft (soft.get()!null));// 内存够时仍存活System.out.println( weak (weak.get()!null));// 通常被回收System.out.println( phantom (phantom.get()!null));// null// 从队列中取出已回收的引用Reference?ref;while((refqueue.poll())!null){System.out.println( 回收通知: ref.getClass().getSimpleName());}}}典型输出内存充足时Before GC: strong true soft true weak true phantom false After GC: strong true soft true weak false phantom false 回收通知: WeakReference 回收通知: PhantomReference软引用在内存充足时不会被回收只有在 OOM 前才被清理弱引用一遇 GC 即清虚引用的 referent 被回收后会触发入队。实践要点1. 缓存优先用 Caffeine / Guava虽然 SoftReference 可以做缓存但回收时机依赖 GC 策略不便于容量规划。生产场景更推荐 Caffeine它支持weakKeys()/weakValues()/softValues()配置并自带 LRU/LFU 策略可控性远高于手工实现。2. WeakHashMap 不是并发安全的WeakHashMap没有同步机制并发场景需用Collections.synchronizedMap包裹或改用ConcurrentHashMapWeakReference自行实现。3. ReferenceQueue 必须主动消费如果关联了队列但从不poll队列中的 Reference 对象会一直占用内存——这是隐性泄漏。Cleaner 内部已经处理了这个问题所以优先用 Cleaner。4. 虚引用的 referent 在入队前已被回收注意JDK 实现中PhantomReference 是在 referent 被** finalize 后、内存释放前**入队的。这意味着此时 referent 已无法访问但仍占用内存直到 Cleaner 执行完。所以虚引用不能用于复活对象。5. 软引用在 G1/ZGC 下的行为G1 对软引用采用分区回收策略可能延迟回收部分软引用ZGC 则在并发标记阶段处理。生产中如果发现软引用占用偏高可调整SoftRefLRUPolicyMSPerMB或改用显式 TTL 缓存。小结强引用永不回收是日常引用的默认形态也是泄漏主因。软引用在 OOM 前回收适合内存敏感的缓存存活时长由SoftRefLRUPolicyMSPerMB控制。弱引用下次 GC 即回收WeakHashMap与ThreadLocal都基于它。虚引用无法获取对象仅在对象回收前通过 ReferenceQueue 通知配合Cleaner替代finalize()。ReferenceQueue 是引用与回收通知的桥梁必须主动消费。下一篇我们将从判定对象存活过渡到如何回收深入对比四种经典垃圾回收算法。更多内容JVM调优实战

相关新闻

【JVM原理详解】22-GC-Roots详解

【JVM原理详解】22-GC-Roots详解

22-GC Roots 详解 上一篇讲到,可达性分析从一组"确定存活"的根对象出发,去遍历整张对象图。这组根对象就是 GC Roots。但到底哪些对象能成为 GC Roots?这并不是一句"栈里的引用"就能说清楚的——HotSpot 实际定义了多类…

2026/10/14 16:43:40 阅读更多 →
ClearerVoice-Studio语音处理引擎架构深度解析:多模态融合与实时噪声抑制技术实现

ClearerVoice-Studio语音处理引擎架构深度解析:多模态融合与实时噪声抑制技术实现

ClearerVoice-Studio语音处理引擎架构深度解析:多模态融合与实时噪声抑制技术实现 【免费下载链接】ClearerVoice-Studio An AI-Powered Speech Processing Toolkit and Open Source SOTA Pretrained Models, Supporting Speech Enhancement, Separation, and Targe…

2026/10/9 20:34:07 阅读更多 →
Kimi 长文本稳赢、豆包搜索翻车?国产三强在 Taotoken 平台的 4 维实测报告

Kimi 长文本稳赢、豆包搜索翻车?国产三强在 Taotoken 平台的 4 维实测报告

三大模型实战横评:Kimi、豆包、通义千问在Taotoken平台的技术角力 上个月用Taotoken同时接入Kimi、豆包和通义千问API进行深度测试时,我们意外发现三家大模型的技术特性差异远超预期——有的模型在专业领域表现惊艳却在基础功能上漏洞百出。经过72小时高…

2026/10/13 21:35:50 阅读更多 →

最新新闻

OpenSandbox:一站式沙箱开发环境如何终结环境一致性难题

OpenSandbox:一站式沙箱开发环境如何终结环境一致性难题

做开发这么多年,我最大的体会是:真正拖慢项目的,往往不是业务逻辑,而是开发环境。团队里每个人都有一台配置不太一样的电脑,依赖版本差一点、系统缓存多一点,同一个项目就可能出现“在我机器上是好的”这种…

2026/10/14 16:43:55 阅读更多 →
2026商洛景区古建牌坊检测排名 TOP5 CMA 资质机构提供牌坊裂缝检测、牌坊倾斜检测、老化检测 联系方式推荐

2026商洛景区古建牌坊检测排名 TOP5 CMA 资质机构提供牌坊裂缝检测、牌坊倾斜检测、老化检测 联系方式推荐

在商洛这片承载着秦风楚韵的土地上,古建牌坊星罗棋布,或矗立于景区要道,或静卧于乡野村口。然而,面对鳞次栉比的检测机构,不少业主深感鱼龙混杂、良莠难辨。无论是景区石牌坊的结构安全鉴定,还是乡村古牌坊…

2026/10/14 16:43:55 阅读更多 →
食品灌装机械不锈钢阀体与分配器三维扫描检测怎么做

食品灌装机械不锈钢阀体与分配器三维扫描检测怎么做

食品灌装机械不锈钢阀体与分配器三维扫描检测怎么做灌装车间里的不锈钢阀体和分配器,很少是因为某一项尺寸超差被换下来的。更多时候是阀芯装入后偏磨、密封面有轻微内漏、分配器对接处出现肉眼看不准的间隙,维修人员拿塞尺和卡尺反复量,也说…

2026/10/14 16:43:55 阅读更多 →
Windows下搭建SonarQube与Sonar Scanner代码质量平台全攻略

Windows下搭建SonarQube与Sonar Scanner代码质量平台全攻略

写代码的人大概都经历过这种事:项目上线前看着代码没啥问题,一上线就出幺蛾子。后来团队引入静态代码分析,把检查环节卡在提交之前,情况才算好转。我在 Windows 环境下从零搭过 SonarQube 和 Sonar Scanner 的组合,踩了…

2026/10/14 16:43:55 阅读更多 →
Neo4j社区版高可用实战:零许可成本构建RTO<5分钟图谱服务

Neo4j社区版高可用实战:零许可成本构建RTO<5分钟图谱服务

1. 为什么社区版也要谈“高可用”——一个被长期误解的现实命题很多人看到“Neo4j 社区版高可用搭建”这个标题,第一反应是皱眉:“社区版不是单机版吗?连集群功能都不支持,谈什么高可用?”这话没错——从官方功能矩阵看…

2026/10/14 16:43:55 阅读更多 →
windows_exporter net 采集器详解:Windows 网络接口流量、状态与告警的完整实现

windows_exporter net 采集器详解:Windows 网络接口流量、状态与告警的完整实现

可观测性指标监控 【免费下载链接】windows_exporter Prometheus exporter for Windows machines 项目地址: https://gitcode.com/gh_mirrors/wi/windows_exporter 点击查看 免费下载 本文围绕 windows_exporter 的 net 采集器展开:它默认启用&#xff…

2026/10/14 16:42:54 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →