Java Finalization‘s Memory-Retention Issues 及Reference类解析
引言《Effective Java Programming Language Guide》 一书中强烈建议不要使用java的finalize()方法去做对象消亡前的清理。因为jvm调用finalize()方法的时机并不确定容易导致Memory-Retention Issues。通俗点讲就是内存没办法及时回收。详细的见oracle的官方说明https://www.oracle.com/technical-resources/articles/javase/finalization.html。Memory-Retention Issues问题简述如果类有重写finalize()方法JVM会将该类的对象标记为finalizable区别于普通对象被垃圾收集器判定为不可达时会立即回收内存finalizable的对象会经过更多的GC周期。下图来自oracle官方。finalizable对象回收通常经历以下过程垃圾回收器检查到该对象不可达垃圾回收器将该对象加入到finalization队列对象变成可达专门的Finalizer线程从finalization队列中将对象移除并执行对象的finalize()方法,并将对象标记为finalized垃圾回收器再次发现该对象不可达而且为finalized状态的(finalize方法已执行过)因此直接回收该对象内存。以上过程的一些个人分析jvm为什么不直接在回收对象前调用finalize()方法而是使用专门的线程去执行java每次GC需要通过可达性分析标记大量对象回收内存后还涉及内存整理如果把finalize()方法的调用也放到这个过程中GC耗时会更长影响系统的响应时间所以只能由另外的过程去处理。Memory-Retention问题从上述过程可以看到finalizable对象至少要2个GC周期才能将对象回收掉。更有甚者如果系统中有大量的对象是finalizable或者有些对象finalize()方法本身就比较耗时加上只有一个Finalizer线程这个线程优先级并不比别的高还会和其他线程竞争执行资源对象在finalization队列中呆的时间更长。在这期间如果有发生GC垃圾收集器也是无法清理这些对象的因为这些对象还在被finalization队列强引用。所以容易产生Memory-Retention问题。Memory-Retention问题解决方案《Effective Java Programming Language Guide》建议使用JDK的Cleaner来做对象消亡前的清理其基于PhantomReference下面系统的介绍JDK的Reference。更新《Effective Java Programming Language Guide》里面也不建议使用Cleaner了Reference解析java的Reference的相关子类用于应用层与垃圾收集器有更多的交互通俗点讲就是垃圾收集器给应用层暴露一些API让应用对对象的回收时机有了一定的控制能力。java官方文档对Reference引入的目的说明如下Provides reference-object classes, which support a limited degree of interaction with the garbage collector. A program may use a reference object to maintain a reference to some other object in such a way that the latter object may still be reclaimed by the collector. A program may also arrange to be notified some time after the collector has determined that the reachability of a given object has changed.翻译如下提供引用对象类支持与垃圾收集器进行有限程度的交互。程序可以使用引用对象来维护对某个其他对象的引用以便后一个对象仍然可以被收集器回收。程序还可以安排在收集器确定给定对象的可达性已更改之后的某个时间收到通知。原文见https://docs.oracle.com/javase/6/docs/api/java/lang/ref/package-summary.html#reachabilitySoftReference垃圾回收器会根据内存使用情况对软引用对象进行回收当然jvm会尽可能的不回收软引用对象至于什么情况下回收JDK并没有明确说明。根据其特点可以看出SoftReference可以用于缓存设计缓存这样不用自己去设计LRU等算法对缓存进行清理。一旦垃圾收集器认为软引用对象需要被清理时JVM会解除软引用对对象的引用(即将referent字段置为null)同时或者稍后将软引用自己放到ReferenceQueue(如果创建软引用时有传入)JDK保证在jvm抛出OutOfMemoryError前清掉所有的软引用对象。除此之外并不保证软引用对象被清理的时间点而且也不保证软引用对象清理的顺序。SoftReference在JDK中实际使用场景如上述介绍SoftReference适合用在缓存场景可以看到JDK中也确实是这样用的大家可以挑ImageSoftReference去看看源码因为代码比较简单这里就不过多介绍了。WeakReference一旦垃圾回收器检测到对象只有弱引用会立即解除弱引用(将弱引用的referent字段置为null)同时或者稍后将弱引用放入ReferenceQueue(如果创建软引用时有传入)。弱引用不能阻止垃圾回收器对对象的回收因此弱引用一般用于“规范化映射(canonicalizing mappings)”,例如WeakHashMap。关于规范化映射的解释何谓规范化映射这个大家可以问一下千问可以得到很形象的回答。我这里总结一下规范化映射是指逻辑上相等的对象在内存中只存在一份其他使用的地方都指向这一个对象。比如像商城类系统一般会定义商品类型。假设为Class Product。对于某个具体的商品比如说A。如果对于A商品在内存中只有唯一的一份实例Product类型。那么我们就称为规范化映射。StringID_AxxxProductAnewProduct(ID_A,xx,xx);MapmapnewHashMap();map.put(ID_A,A);doBusiness(A);从上述代码可以看到对于商品A在内存中只有唯一的一份其他所有地方对商品A的引用都是指向这唯一的实例。这样就有一个特点一旦指向A的引用置空后应用层无法在建立起对商品A对象的引用。对canonicalizing mappings详细说明可以阅读https://objectcomputing.com/resources/publications/sett/june-2000-collaborating-with-the-java-memory-managerhttps://wiki.c2.com/?CanonicalizedMappingWeakReference在JDK中实际使用场景如上所述WeakReference用于规范化映射场景一旦对象强引用全部解除后应用在也无法访问到该对象此时跟该对象关联的资源如果需要清理的话可以借助WeakReference得到通知从而有时机进行资源清理。简单来讲就是一个对象会被多处引用一旦所有的强引用被解除后才能清理资源此时利用WeakReference机制更容易写代码。不然应用层自己得维护引用计数当计数为0后执行清理动作。JDK中的WeakHashMap有使用到WeakReference这样可以自动清理WeakHashMap中无效的Entry。如下图WeakHashMap的Entry是WeakReference一旦对象的强引用被置空后根据WeakReference的特性JVM会自动清除WeakReference对其引用将对象回收。至于Entry本身此时也没有用处需要从WeakHashMap中清理掉该逻辑可以跟踪java.util.WeakHashMap#expungeStaleEntries函数。PhantomReference虚引用在垃圾回收器确定其引用对象可以被回收后放到ReferenceQueue这一点与SoftReference及WeakReference有所区别。对于finalizable对象后两者在垃圾回收器将对象放入finalization队列时就会解除引用并将引用放入到ReferenceQueue。而PhantomReference必须在finalizable对象从finalization队列移除后并且被垃圾回收器再次检测到不可达能够真正的回收其内存时放入到ReferenceQueue中而且引用不会自动解除。PhantomReference在JDK中实际使用场景PhantomReference不会被jvm自动清理引用关系的猜测PhantomReference引入的目的是用于做对象回收前的清理因此得继续保持引用关系让JVM无法回收内存然后应用层代码得到入队通知后调用相关清理逻辑(可能涉及到访问被回收对象的相关字段所以清理逻辑执行完对象不能被回收不然就相当于c语言中的指针越界访问了)并手工清除引用让对象彻底不可达然后被JVM回收。基于上述推测对JVM来说对象的内存能够被回收的前提是没有任何引用指向它包括强引用、软引用、弱引用、虚引用。该推测也在oracle的官方文档得到印证。https://docs.oracle.com/javase/6/docs/api/java/lang/ref/package-summary.html#reachabilityGoing from strongest to weakest, the different levels of reachability reflect the life cycle of an object. They are operationally defined as follows:1、 An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strongly reachable by the thread that created it.2、 An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.An object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference. When the weak references to a weakly-reachable object are cleared, the object becomes eligible for finalization.3、 An object is phantom reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.4、Finally, an object is unreachable, and therefore eligible for reclamation, when it is not reachable in any of the above ways.关于各种Reference类加入队列时机的验证验证代码如下publicclassDemoApplication{publicstaticvoidmain(String[]args){ReferenceQueueObjectreferenceQueuenewReferenceQueue();SoftReferenceObjectweakReferencenewSoftReference(newObject(){privateinta10;protectedvoidfinalize()throwsThrowable{// 排除线程调度对执行顺序的影响Thread.sleep(10000);System.out.println(-----------finalize----);}},referenceQueue);newThread(()-{try{Reference?removedreferenceQueue.remove();System.out.println(-----------referenceQueue----);}catch(InterruptedExceptione){e.printStackTrace();}}).start();while(true){System.gc();}}}SoftReference验证结果可以看到程序一直不打印日志。因为代码就创建了一个对象内存是充足的所以JVM不会去尝试回收软引用对象。WeakReference验证结果从第一个图可以看到jvm会自动清除引用关系第二个图可以看到jvm将弱引用入队、并清除引用关系发生在finalize之前。PhantomReference验证结果从第一个图可以看到jvm不会自动清除引用关系第二个图可以看到jvm将虚引用入队发生在finalize之后。所以应用层从引用队列中移出PhantomReference后一定要及时调用clear()函数解除引用。PS以上内容是基于JDK8当时发现PhantomReference得到通知时还引用着对象我以为PhantomReference就是利用这个时机调用对象清理函数释放资源但实际上又没办法拿到它觉得这个设计有点矛盾通过ai得到回答这个也是jvm不得已为之有兴趣的大家可以通过AI对话了解一下背景。jdk21PhantomReference对象得到通知时引用关系已经清除了这一点跟jdk8有点区别。反思通过PhantomReference实现对象消亡前的清理工作对比finalize()方法到底有什么优势无论是PhantomReference还是finalize()都无法降低清理工作的计算量总工作量不变的情况下要实现更快的清理速度那只能使用并发个人认为这是PhantomReference相对finalize()唯一优势。finalize()只能由单独的Finalizer线程执行清理逻辑而PhantomReference由应用层代码执行可以采用多线程增加并发度。但是PhantomReference要求应用层在将弱引用移出引用队列后及时调用clear()函数这个开发者很容易忽略而埋下潜在的风险。所以《Effective Java Programming Language Guide》推荐类定义清理函数然后由开发者主动调用finalize()函数作为最终的兜底。备注本文有些地方言辞可能并不严谨作者是刻意没去发散细节一旦要对所有细节都做阐述及证明那这边文章很难写完。本文主要只是想简明的阐述Memory-Retention问题以及Reference引入的目的及使用场景。文章中大部分是基于JDK的类注释及参考文档的理解写的该篇文章由于英文水平一般所以文中有些地方读起来可能不太好理解建议大家反复阅读参考文档及jdk的注释相互印证这样才能彻底理解英文文献想要表达的意思。参考文档汇总https://docstore.mik.ua/orelly/java-ent/jnut/ch13_01.htmhttps://www.oracle.com/technical-resources/articles/javase/finalization.htmlhttps://wiki.c2.com/?CanonicalizedMappinghttps://objectcomputing.com/resources/publications/sett/june-2000-collaborating-with-the-java-memory-manager

相关新闻

2026年有哪些真正好用的AI写小说软件?10款热门AI小说生成器深度测评(内含工具优缺点对比图)

2026年有哪些真正好用的AI写小说软件?10款热门AI小说生成器深度测评(内含工具优缺点对比图)

现在随便一搜写小说的软件简直满天飞,不少小伙伴盲目跟风下载,结果用了不到两天就在评论区疯狂跟我倒苦水:写出来的情节乱七八糟,主角人设更是前后打架完全没法看! 讲真,新手开始写小说真的需要一套靠谱的…

2026/10/31 19:02:31 阅读更多 →
DevExpress WinForms TreeMap组件,用嵌套矩形可视化复杂分层数据

DevExpress WinForms TreeMap组件,用嵌套矩形可视化复杂分层数据

DevExpress WinForms TreeMap控件允许用户使用嵌套的矩形来可视化复杂的平面或分层数据结构。 DevExpress WinForms有180组件和UI库,能为Windows Forms平台创建具有影响力的业务解决方案。同时能完美构建流畅、美观且易于使用的应用程序,无论是Office风…

2026/10/28 18:01:53 阅读更多 →
3.3 万亿市值的长鑫科技:是国产存储里程碑,还是资本市场情绪泡沫?

3.3 万亿市值的长鑫科技:是国产存储里程碑,还是资本市场情绪泡沫?

长鑫科技(688825)登陆科创板短短数日,总市值站稳 3.3 万亿级别,一举超越工商银行登顶 A 股市值榜首,刷新 A 股单日成交纪录,成为当下市场争议最大的硬科技标的。这份万亿估值,一半是国产 DRAM 突…

2026/10/28 18:01:54 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/31 0:19:11 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/31 10:20:19 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/31 10:20:23 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/31 0:59:28 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/31 10:30:19 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/31 9:29:43 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 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/31 18:03:41 阅读更多 →
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/31 11:00:40 阅读更多 →
黑夜航拍船只数据集训练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/31 0:09:01 阅读更多 →