尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
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
RELATED

相关推荐

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

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

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

📅 2026/8/8 18:00:38
DevExpress WinForms TreeMap组件,用嵌套矩形可视化复杂分层数据

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

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

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

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

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

📅 2026/9/17 21:16:13
MORE NEWS

更多资讯

📰

TensorFlow 2024生存指南:安装部署、生态对比与实战路线

TensorFlow在2024年到底是什么处境,还有没有必要从零开始学,这个问题我几乎每天都能看到有人在讨论。先说结论:TensorFlow依然是工程化和生产部署领域绕不开的主力框架,而且在移动端、嵌入式设备上它有明显的生态壁垒。但如果你是…

📰

阿里云部署OneApi:用Docker跑通TaoToken统一Key通道的配置骨架

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

📰

LLM Agent记忆系统实战:Hindsight架构、MCP工具链与Docker部署

1. 从“hindsight”说起:为什么我们需要给Agent装上“后视镜”“hindsight”这个词,直译过来就是“后见之明”。放在LLM Agent的语境里,它指向一个非常具体且要命的问题:Agent的记忆到底该怎么管。我接触过不少做Agent落地的团队&…

📰

Conda与pip环境管理实战:从环境隔离到镜像源配置与报错排查

用Conda和pip创建Python环境,这事儿听起来像Python开发里最基础的操作,可我实际收到的求助里,十有七八都是卡在这一步。不是不会敲conda create和pip install,而是搞不清这两个工具的分工,更不知道装完之后还要初始化、…

📰

ArcGIS避坑指南:从安装许可到空间分析的高频问题排查手册

做GIS这些年,我见过太多人不是倒在复杂分析上,而是被安装、坐标系、数据转换这些“小事”卡到怀疑人生。ArcGIS这套东西功能确实强,但老牌大型软件的脾气也一点不少:安装讲究顺序、环境挑剔路径、坐标系一步错步步错。之前我在零散…

📰

记忆张量亿元Pre A融资后,AI记忆如何成为Agent的长期基础设施

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬