finalize() 原理

article/2025/9/14 2:31:41

finalize 方法的作用是:

如果对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,那他将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。
注意:当对象没有覆盖 finalize 方法,或者 finalize 方法已经被虚拟机调用过,虚拟机将这两种情况都视为 “没有必要执行”。也就是说,finalize 方法只会被执行一次。

如果这个对象被判定为有必要执行 finalize 方法,那么这个对象将会放置在一个叫做 F-Queue 的队列之中,并在稍后由一个虚拟机自动建立的,优先级为 8 的 Finalizer 线程去执行它。
注意:如果一个对象在 finalize 方法中运行缓慢,将会导致队列后的其他对象永远等待,严重时将会导致系统崩溃。

finalize 方法是对象逃脱死亡命运的最后一道关卡。稍后 GC 将对队列中的对象进行第二次规模的标记,如果对象要在 finalize 中 “拯救” 自己,只需要将自己关联到引用上即可,通常是 this。
如果这个对象关联上了引用,那么在第二次标记的时候他将被移除出 “即将回收” 的集合;如果对象这时候还没有逃脱,那基本上就是真的被回收了。

 

如果类中重写了finalize方法,当该类对象被回收时,finalize方法有可能会被触发,下面通过一个例子说明finalize方法对垃圾回收有什么影响

publicclassFinalizeCase{privatestaticBlock holder =null;public static void main(String[] args)throwsException{holder =newBlock();holder =null;System.gc();//System.in.read();}staticclassBlock{byte[] _200M =newbyte[200*1024*1024];}}

Block类中声明一个占用内存200M的数组,是为了方便看出来gc之后是否回收了Block对象,执行完的gc日志如下:

从gc日志中可以看出来,执行完System.gc()之后,Block对象被如期的回收了,如果在Block类中重写了finalize方法,会是一样的结果么?

staticclassBlock{byte[] _200M = new byte[200*1024*1024];@Overrideprotected void finalize()throwsThrowable{System.out.println("invoke finalize");}}

执行完成gc日志如下:

和之前的gc日志进行比较,发现finalize方法确实被触发了,但是Block对象还在内存中,并没有被回收,这是为什么?

下面对finalize方法的实现原理进行分析。

finalize实现原理

1、对象的初始化过程

会对has_finalizer_flag和RegisterFinalizersAtInit进行判断,如果类重写了finalize方法,且方法体不为空,则调用register_finalizer函数,继续看register_finalizer函数的实现:

其中Universe::finalizer_register_method()缓存的是jdk中java.lang.ref.Finalizer类的register方法,实现如下:

在jvm中通过JavaCalls::call触发register方法,将新建的对象O封装成一个Finalizer对象,并通过add方法添加到Finalizer链表头。

对象O和Finalizer类的静态变量unfinalized有联系,在发生GC时,会被判定为活跃对象,因此不会被回收回收

FinalizerThread线程

在Finalizer类的静态代码块中会创建一个FinalizerThread类型的守护线程,但是这个线程的优先级比较低,意味着在cpu吃紧的时候可能会抢占不到资源执行。

FinalizerThread线程负责从ReferenceQueue队列中获取Finalizer对象,如果队列中没有元素,则通过wait方法将该线程挂起,等待被唤醒

如果返回了Finalizer对象,执行对象的runFinalizer()方法,其实可以发现:在runFinalizer()方法中主动捕获了异常,即使在执行finalize方法抛出异常时,也没有关系。

通过hasBeenFinalized方法判断该对象是否还在链表中,并将该Finalizer对象从链表中删除,这样下次gc时就可以把原对象给回收掉了,最后调用了native方法invokeFinalizeMethod,其中invokeFinalizeMethod方法最终会找到并执行对象的finalize方法。

ReferenceHandler线程

有个疑问:既然FinalizerThread线程是从ReferenceQueue队列中获取Finalizer对象,那么Finalizer对象是在什么情况下才会被插入到ReferenceQueue队列中?

Finalizer的祖父类Reference中定义了ReferenceHandler线程,实现如下:

当pending被设置时,会调用ReferenceQueue的enqueue方法把Finalizer对象插入到ReferenceQueue队列中,接着通过notifyAll方法唤醒FinalizerThread线程执行后续逻辑,实现如下:

2、pending字段什么时候会被设置?—— 在GC过程的引用处理阶段

在GC过程的引用处理阶段,通过oopDesc::atomic_exchange_oop方法把发现的引用列表设置在pending字段所在的地址

Finalizer导致的内存泄漏

平常使用的Socket通信,SocksSocketImpl的父类重写了finalize方法

这么做主要是为了确保在用户忘记手动关闭socket连接的情况下,在该对象被回收时能够自动关闭socket来释放一些资源,但是在开发过程中,真的忘记手动调用了close方法,那么这些socket对象可能会因为FinalizeThread线程迟迟没有执行到这些对象的finalize方法,而导致一直占用某些资源,造成内存泄露。   

引用

链接:https://www.jianshu.com/p/9d0552032cf3
链接:https://www.jianshu.com/p/74224cb0120f


http://chatgpt.dhexx.cn/article/s3wB1QCE.shtml

相关文章

finalize()

注:本文的目的并不是鼓励使用finalize方法,而是大致理清其作用、问题以及GC执行finalize的过程。 1. finalize的作用 finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。fina…

java finalize方法详解

1. finalize的作用 finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。finalize()与C中的析构函数不是对应的。C中的析构函数调用的时机是确定的(对象离开作用域或delete掉)&…

java中finalize()方法

finalize 垃圾回收机器(Garbage Collection),也叫GC,垃圾回收器主要有一下特点: 当对象不再被程序所使用的时候,垃圾回收器将会将其回收垃圾回收是在后台运行的,我们无法命令垃圾回收器马上回收资源&…

Finalize详解

finalize()方法详解,前言,finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。 finalize的作用: (1)finalize()与C中的析构函数不是对应的。C中的析构函数调用的时机是确定的…

finalize的理解

finalize的理解 一般的回答:它是Object中的一个方法,子类重写它,垃圾回收时候方法会被调用,可以再其中进行一些资源的解释和清理工作。 优秀的回答:将资源的释放和清理放在finalize方法中是非常不好的,影…

Java FX swt_DOC-13-08 JavaFX与SWT的协作性

DOC-13-08 JavaFX与SWT的协作性 本章展示了如何在SWT应用程序中加入一个JavaFX场景图,以及如何使SWT和JavaFX控件协作。 介绍 如果你开发SWT应用程序,你知道SWT使用本地操作系统的控件,而且并不能简单的配置来使用高级GUI特性,比如…

Java SWT 表格Table如何动态显示信息

让Table显示信息用到的是TableItem类。创建一个TableItem类对象,通过调用该对象的setText( new String[ ] )方法可以显示一行数据,循环调用则可以显示多条不同的数据。 一、步骤: 1. 创建Table类 。最好将Table类设置为全局变量。并且设置该表格有多少列…

Java ——SWT利用DateTime获取日历控件

1、话不多说,先看效果图: 19是我选择的日期,其他功能就不一一介绍了,这个看你们自己。 2、Test01界面: Test01代码如下: package test; import org.eclipse.swt.widgets.Display; import org.eclipse.swt…

shell swt 样式_SWT之路:SWT图像显示

简明现代魔法 -> Java编程语言 -> SWT之路:SWT图像显示 SWT之路:SWT图像显示 2009-10-03 程序演示 还是先用SWT Desiner创建界面程序。然后创建一个Display对象和Image对象,和一个GC对象。类org.eclipse.swt.graphics.GC是一个封装了所…

eclipse java swt_Eclipse下搭建SWT开发环境

0.序言 还是老风格,从头写些基本的东西,帮助自己,也帮助正处于困惑中的别人。今天介绍的是Eclipse下的SWT的配置过程。自己前两天要做个项目,配置了半天都不正确,后来慢慢总结了一下,不同环境配置的方法可能…

java swt button_JAVA.SWT/JFace: SWT基本组件之按钮(Button)

《Eclipse SWT/JFACE 核心应用》 清华大学出版社 5.2 按钮(Button) 按钮有普通按钮(SWT.PUSH)、单选按钮(SWT.RADIO)、多选按钮(SWT.CHECK)、箭头按钮(SWT.ARROW)和切换按钮(SWT.TOGGLE)几种类型。 同时,也可以设置按钮的样式。设置按钮文字对齐的样式有SWT.LEFT、S…

Java SWT 表格Table实时刷新数据

一、动态展示数据 当对表格展示的数据进行删除和增加的时候,想实时进行表格数据更新显示。用到的方法是,首先将表格数据全部删掉,然后在读取数据库最新的数据显示到表格中。  可以将显示表格信息的代码封装成一个方法,当对当前表…

Java SWT封装Table显示数据

一个表格就类似于一张二维表。第一行为关系模型,就是每一列的列名。从第二行开始就是表的数据,简称元组。下面实现对其Table的封装。 首先显示一个窗口。创建一个Table。将在showTableInfos()方法对Table表格进行封装。 import org.eclipse.swt.widgets.…

SWT控件总结

控件palette 1.System Choose component 选择组件:允许选择组件类型并将其拖放到设计画布上 Tab Order标签的顺序:设置所选选项卡顺序 2.Composites Composite 复合:能够包含其他控件的控件 Group 集团:提供带有可选标题的蚀…

SWT和JFace应用笔记

SWT和JFace应用笔记 链接:https://pan.baidu.com/s/1To4Lhgan4xEr1iaFA0Rerg 提取码:63qm 1.SWT笔记 一.创建一个SWT程序有3个部分 1.初始化窗口:首先创建Dispaly对象和Shell对象。Display:对象封装了调用操作系统的有关方法Shell&#xf…

matlab swt函数,matlab swt 函数出错

matlab swt 函数出错 我在用matlab swt 函数分解信号时总是出现以下错误,麻烦各位高手告知该怎么修改,swt函数如何ERROR ... ---------------------------------------------------------------------------------------------------------------------…

【SWT】自定义数据表格

目的 使用SWT技术自定义数据表格,本文抛砖引玉,给出了SWT构建数据表格的基本思路和简单实现。更多特殊需求即表格功能实现待续…… 思路 数据表格由表格头与表格体两边部分组成。 表格头部分是固定的,其字段右侧包含一个简单的表格工具–…

【SWT】Lable 文字折行

目标 当Label 中的文字过多时,使得文字折行显示。 效果如图所示: 分析与实践 Label 自带样式是一行显示所有信息。当一行显示不下时,超出部分会被隐藏掉,当Label有足够长度时再将其展示出来。Label这种处理超出部分的方式很粗…

java swt 几种布局_实战SWT布局

fortune 阅读(577) 评论(0) 编辑 收藏 所属分类: java技术 标准的SWT布局类FillLayout:在容器中以相同的大小单行或单列的排列组件 RowLayout:以单行或多行的方式使用几个选项(fill,wrap,spacing,justify,type)定制组件的排列方式 GridLayout&#xff…

【SWT】内容分割线

目标 SWT 容器中画横向直线或竖向直线将容器中的内容分割开来。本文介绍了官方的两个示例,效果图见实践部分。 分析 SWT 中至少由两种方法画直线(横向或纵向) SWT 中的GC可以画直线 这种方法最容易想到,但实现起来比较麻烦&a…