问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

JVM源码分析之警惕存在内存泄漏风险的FinalReference(增强版)

发布网友 发布时间:22小时前

我来回答

1个回答

热心网友 时间:21小时前

JAVA对象引用体系除了强引用之外,还特地实现了一系列其他引用以满足性能和可扩展性需求:SoftReference、WeakReference、PhantomReference、FinalReference。本文将重点探讨FinalReference,因其在内存分析工具如MAT中的高占用率引起了广泛关注。在JDK中,FinalReference的实现为package级别访问权限,实际使用中我们无法直接扩展。然而,为了满足特定需求,JDK通过扩展实现了一个名为java.lang.ref.Finalizer的类,同样具有package级别访问权限且为final,确保不可被扩展。接下来,我们将围绕Finalizer类展开讨论,关注其构造函数、对象注册过程以及内存管理机制。

首先,让我们关注Finalizer的构造函数。构造函数具有private访问权限,意味着我们无法自行创建此类实例。构造函数接收一个参数,即FinalReference指向的对象引用。在构造函数中,FinalReference对象被插入到Finalizer对象链中,这是一个静态关联,表明在该链中的对象无法被垃圾回收(GC)处理,除非通过某种方式解除引用关系,因为Finalizer类无法被卸载。

虽然我们无法直接创建Finalizer对象,但有一个名为register的静态方法,它在方法内部创建此类对象,并将其加入到Finalizer对象链。这个方法由虚拟机(VM)调用。那么,VM在什么情况下会调用这个方法呢?答案与类的修饰符有关,特别是final修饰符。当一个类被final修饰时,我们称之为f类。在处理f类对象时,GC会在对象被回收前调用其finalize方法。

接下来,我们探讨如何判断一个类是否为f类。在Java世界中,所有类继承了名为finalize的方法,即使没有覆写该方法,也会继承此方法。然而,判断一个类是否为f类的标准并不仅仅是包含一个参数为空、返回值为void的名为finalize的方法。另一个关键点是finalize方法必须非空。因此,虽然Object类包含一个finalize方法,但它本身并非f类,其对象在GC回收时不调用其finalize方法。

需要注意的是,f类对象的创建过程涉及多个步骤,例如在表达式如“new A(2)”中,对象首先被分配空间,然后调用构造函数。在这个过程中,我们可以通过设置VM参数-XX:RegisterFinalizersAtInit来选择在构造函数调用前或对象空间分配后将对象注册到Finalizer对象链中。默认情况下,此参数为true,意味着在构造函数返回前注册。若关闭此参数,注册将在对象空间分配后立即进行。

Hotspot实现中,通过在Object类初始化时替换构造函数的return指令为_return_register_finalizer指令,这是一个非标准字节码指令,用于在处理此指令时调用Finalizer.register方法。这一实现巧妙地解决了对所有类构造函数的侵入性问题。

在GC回收过程中,f类对象通过FinalizerThread线程处理。该线程从queue中取出Finalizer对象,执行runFinalizer方法,将Finalizer对象从链中剥离,并传递给native方法invokeFinalizeMethod,调用f对象的finalize方法。整个过程紧密相连,确保f类对象在合适时机执行清理逻辑。

Finalizer对象何时被放入ReferenceQueue?当GC发生时,它会判断f类对象是否仅由Finalizer类引用。若满足条件,表示即将被回收且可以执行finalize方法,Finalizer对象将被放入Reference.pending字段中。然而,f类对象并未真正被回收,因为Finalizer类仍保持对其引用。在GC完成后,jvm通过调用ReferenceHandler线程的wait方法唤醒,该线程处理Reference.pending为空时调用notify方法,最终将Finalizer对象放入Finalizer类的ReferenceQueue中,从而触发FinalizerThread执行后续逻辑。

关于f对象的finalize方法抛出异常,实际上不会导致FinalizerThread退出,因为在runFinalizer方法中对Throwable异常进行了捕获处理。

如果我们在f对象的finalize方法中重新将当前对象赋值为可达状态,当此f对象再次变为不可达时,它不会再执行finalize方法。因为在执行完第一次方法后,f对象与之前绑定的Finalizer对象关系被解除,下次GC时不会再发现该对象与Finalizer对象的关联,从而不会再次调用其finalize方法。

Finalizer可能导致内存泄漏的问题。以Socket通信为例,SocksSocketImpl的父类实现了finalize方法,其主要目的是在对象被回收前主动关闭socket以释放资源。然而,如果用户忘记关闭socket,导致内存泄露。多次遇到此类问题后,建议避免在运行期不断创建f对象,以防止悲剧发生。

总结而言,Finalizer实现了析构函数的概念,允许我们在对象被回收前执行清理逻辑。然而,这种机制对对象生命周期和GC过程产生了影响。了解Finalizer的实现细节有助于我们更好地管理内存使用,避免潜在的内存泄漏问题。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
叶罗丽精灵梦小游戏有吗? 女孩爱玩化妆换衣服的游戏 洋娃娃类似的游戏推荐 女生换装小游戏有哪些 哪一个换装游戏是古代的宫廷小花仙 女生换装小游戏有哪些 有没有比较好玩的纯古风换装单机游戏啊 父母走了,如何怀念? 如何在失去亲人后怀念他们呢? 父亲过世,如何怀念 U盘损坏数据恢复的有效方法使用专业工具和技巧来恢复U盘中的损坏... ...格式化的U盘中的数据文件简单有效的数据恢复方法和注意事项_百度知 ... 手机格式化恢复技巧教你简单有效地恢复误格式化的手机数据 英特尔® 奔腾® 处理器T4200和T6400有什么区别呢? 英特尔® 奔腾® 处理器T4200和T6400有什么区别呢 笔记本处理器性能比较 英特尔奔腾处理器T4200和英特尔酷睿2 双核处理器T6400哪个更好?? Intel 酷睿2双核T6400比Intel 酷睿2双核T5750在哪方面好? 该选t4200还是t6400? T5750和T6400哪个较好??? CPU T6400与T5750哪个好? 新洲区居住证办理电话号码 辛冲墓群地址在哪里? 新七建设集团有限公司军山创业园地址 深圳龙华新楼盘房价 要练身体心肺功能与力量,什么方式最好? 轻微脂肪肝治疗方法有哪些? ...敢跟他们打他们人多都是高个 我想练散打 我是个运动员体重115... ...看起来特别横那种。ps:本人175,体重差不多115斤。 怎样提高身体耐力? 100斤的人怎样才能背着130的人爬上五楼 五贝子染发膏技术特点及技术优势 30个经典的人生哲理故事,一场精彩的人生课! c5驾校什么地方有 大家帮忙看看,这样的户型怎么样 大家帮我这个个户型,说说缺点和优点,建议出手与否? 大家帮我看下这个小户型 请大家帮忙看看这个户型好妈谢谢大家 请大家帮忙分析下这户型 请大家帮我看看这个房间的户型好不好,有什么优缺点。谢谢。。 请帮我看看这个户型怎么样? 请大家帮我分析一下这种户型怎么样,客厅下面的打叉的方框是什么意思,是... 怎么做好微信营销 最常用的微信营销工具有哪些 常用excel快捷键excel快捷键大全常用整理 电脑表格快捷键excel常用快捷键一览表 Excel快捷键大全,93个会计人员必备快捷键 100个Excel使用快捷键,建议收藏! excel表格的快捷键excel快捷键大全常用整理 excel快捷键大全和excel常用技巧整理 excel的公式快捷键大全常用(Excel办公必须掌握的快捷键和公式) excel快捷键大全(excel快捷键大全常用整理) 学炸串要多少学费 邯郸炸串培训班学费多少