【Java技术专题】「原理专题」深入分析Java中finalize方法的作用和底层原理
finalize方法是什么
finalize方法是Object的protected方法,Object的子类们可以覆盖该方法以实现资源清理工作,GC在首次回收对象之前调用该方法。
finalize方法与C++的析构函数的区别
finalize方法与C++中的析构函数不是对应的,C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),但Java中的finalize的调用具有不确定性,不建议用finalize方法完成“非内存资源”的清理工作。
finalize方法合适清理的对象
- 清理本地对象(通过JNI创建的对象);
- 作为确保某些非内存资源(如Socket、文件等)释放的一个补充,在finalize方法中显式调用其他资源释放方法。
可以触发finalize执行的方法
在Java中含有一些一些与finalize相关的方法,由于一些致命的缺陷,已经被废弃了,如System.runFinalizersOnExit() 方法、Runtime.runFinalizersOnExit() 方法、
System.gc() 与System.runFinalization() 方法。
他们增加了finalize方法执行的机会,但不可盲目依赖它们Java语言规范并不保证finalize方法会被及时地执行、而且根本不会保证它们会被执行finalize方法可能会带来性能问题。
因为JVM通常在单独的低优先级线程中完成finalize的执行。
finalize实现对象再生问题
finalize方法的实现中,可将待回收对象赋值给GC Roots可达的对象引用,从而达到对象再生的目的。
finalize方法至多由GC执行一次(用户当然可以手动调用对象的finalize方法,但并不影响GC对finalize的行为)。
finalize的执行过程(生命周期)
大致描述一下finalize的运行流程:当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。
若对象未执行过finalize方法,将其放入F-Queue队列,由低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。
对象对于finalize方法的两种状态
对象可由两种状态,涉及到两类状态空间,一是终结状态空间 F = {unfinalized, finalizable, finalized};二是可达状态空间 R = {reachable, finalizer-reachable, unreachable}。
终结状态空间
各状态含义如下:
unfinalized: 新建对象会先进入此状态,GC并未准备执行其finalize方法,因为该对象是可达的。
finalizable: 表示GC可对该对象执行finalize方法,GC已检测到该对象不可达。正如前面所述,GC通过F-Queue队列和一专用线程完成finalize的执行。
对应的流程图如下所示:

可达状态空间
各状态含义如下:
- finalized: 表示GC已经对该对象执行过finalize方法
- reachable: 表示GC Roots引用可达
- finalizer-reachable(f-reachable):表示不是reachable,但可通过某个finalizable对象可达
- unreachable:对象不可通过上面两种途径可达
状态变迁图:

变迁说明:
新建对象首先处于[reachable, unfinalized]状态(A)
随着程序的运行,一些引用关系会消失,导致状态变迁,从reachable状态变迁到f-reachable(B, C, D) 或 unreachable(E, F)状态
JVM检测到处于unfinalized状态的对象变成f-reachable或unreachable。
- JVM会将其标记为finalizable状态(G,H)。若对象原处于[unreachable, unfinalized]状态,则同时将其标记为f-reachable(H)。
在某个时刻,JVM取出某个finalizable对象,将其标记为finalized并在某个线程中执行其finalize方法。
由于是在活动线程中引用了该对象,该对象将变迁到(reachable, finalized)状态(K或J)。该动作将影响某些其他对象从f-reachable状态重新回到reachable状态(L, M, N)处于finalizable状态的对象不能同时是unreahable的。
将对象finalizable对象标记为finalized时会由某个线程执行该对象的finalize方法,致使其变成reachable。
注:System.runFinalizersOnExit()等方法可以使对象即使处于reachable状态,JVM仍对其执行finalize方法
代码示例
对象复活
public class GC {
public static GC SAVE_HOOK = null;
public static void main(String[] args) throws InterruptedException {
SAVE_HOOK = new GC();
SAVE_HOOK = null;
System.gc();
Thread.sleep(500);
if (null != SAVE_HOOK) { //此时对象应该处于(reachable, finalized)状态
System.out.println("Yes , I am still alive");
} else {
System.out.println("No , I am dead");
}
SAVE_HOOK = null;
System.gc();
Thread.sleep(500);
if (null != SAVE_HOOK) {
System.out.println("Yes , I am still alive");
} else {
System.out.println("No , I am dead");
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("execute method finalize()");
SAVE_HOOK = this;
}
}
覆盖finalize方法以确保资源释放
作为一个补充操作,以防用户忘记“关闭“资源,JDK中FileInputStream、FileOutputStream、Connection类均用了此”技术“,下面代码摘自FileInputStream类
/**
* Ensures that the <code>close</code> method of this file input stream is
* called when there are no more references to it.
*
* @exception IOException if an I/O error occurs.
* @see java.io.FileInputStream#close()
*/
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
}
注意:我们自己手动调用finalize方法并不会影响到上述内部标记的变化,因此JVM只会至多调用finalize一次,即使该对象“复活”也是如此。我们手动调用多少次不影响JVM的行为
若JVM检测到finalized状态的对象变成unreachable,回收其内存(I),若对象并未覆盖finalize方法,JVM会进行优化,直接回收对象(O)
【Java技术专题】「原理专题」深入分析Java中finalize方法的作用和底层原理的更多相关文章
- Java中finalize()方法的作用
finalize方法是Object提供的的实例方法,使用规则如下: 当对象不再被任何对象引用时,GC会调用该对象的finalize()方法 finalize()是Object的方法,子类可以覆盖这个方 ...
- paip.编程语言方法重载实现的原理及python,php,js中实现方法重载
paip.编程语言方法重载实现的原理及python,php,js中实现方法重载 有些语言,在方法的重载上,形式上不支持函数重载,但可以通过模拟实现.. 主要原理:根据参数个数进行重载,或者使用默认值 ...
- 自己(转)JAVA中toString方法的作用
JAVA中toString方法的作用 因为它是Object里面已经有了的方法,而所有类都是继承Object,所以“所有对象都有这个方法”. 它通常只是为了方便输出,比如System.out.print ...
- Servlet工作原理解析 《深入分析java web 技术内幕》第九章
参考关于servblet的相关文章 侧重概况:https://blog.csdn.net/levycc/article/details/50728921 ibm的相关:https://www.ibm. ...
- Java已五年1—二本物理到前端实习生到Java程序员「回忆贴」
关键词:郑州 二本 物理专业 先前端实习生 后Java程序员 更多文章收录在码云仓库:https://gitee.com/bingqilinpeishenme/Java-Tutorials 前言 没有 ...
- 巧用 .NET 中的「合并运算符」获得 URL 中的参数
获取 URL 中的 GET 参数,无论用什么语言开发网站,几乎是必须会用到的代码.但获取 URL 参数经常需要注意一点就是要先判断是否有这个参数存在,如果存在则取出,如果不存在则用另一个值.这个运算称 ...
- java回收finalize方法的作用(编程思想)
清理:终结处理和垃圾回收 java有垃圾回收期负责回收无用对象占据的内存资源.但也有这种情况:假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收期只知道释放那些由new分配的内存 ...
- JAVA基础篇 之 finalize()方法的作用
我们知道java有垃圾回收器负责回收无用对象占据的内存资源,但也有特殊情况:假设你的对象(并非使用new)获得了一块特殊的内存区域,由于垃圾回收器只知道回收那些经由new分配的内存,所以它不知道如 ...
- Java中finalize方法用途何在?
package thinking.in.java.demo; /* * finalize的用途何在? * *本例的终止条件是L所有的Book对象在被当做垃圾回收前都应该被签入.但是在main方法中 * ...
- JAVA中toString方法的作用
因为它是Object里面已经有了的方法,而所有类都是继承Object,所以“所有对象都有这个方法”. 它通常只是为了方便输出,比如System.out.println(xx),括号里面的“xx”如果不 ...
随机推荐
- VM运行centos网络配置(出现错误)详解
一般按照正常安装流程到这一步的时候可以直接点击网络和主机名,但是我这里并没有连接成功,因此我跳过了这里想着后面再配置 后面通过ping www.baidu.com以及ping 百度的ip地址(如何获得 ...
- instanceof的使用和向下转型
x instanceof A:检验x是否为类A的对象,返回值为boolean型 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instance ...
- 魔改editormd组件,优化ToC渲染效果
前言 我的StarBlog博客目前使用 editor.md 组件在前端渲染markdown文章,但这个组件自动生成的ToC(内容目录)不是很美观,我之前魔改过一个树形组件 BootStrap-Tree ...
- JSTL组件的下载链接地址
配置JSTL和下载jar包 JSTL的安装包 下载地址:http://tomcat.apache.org/download-taglibs.cgi 在下载页面找到JSTL的规范和实现的两个jar包,如 ...
- AgileBoot - 如何集成内置数据库H2和内置Redis
本项目地址: github: https://github.com/valarchie/AgileBoot-Back-End gitee: https://gitee.com/valarchie/Ag ...
- 通过jmeter,将数据库数据查询出来并打印
1. 连接数据库 1.1 添加jdbc驱动路径 1.2 JDBC Connection Configuration 1.3 JDBC Request 2. 查询数据库数据并打印 2.1 定义变量 ...
- 【lwip】12-一文解决TCP原理
目录 前言 12.1 TCP协议简介 12.2 TCP相关的一些概念词 12.2.1 MSL 12.2.2 MSS 12.3 TCP工作特性 12.3.1 面向连接 12.3.2 全双工通信 12.3 ...
- Enum.Parse的使用
Enum的转换,用Enum.Parse() Enum.Parse()方法.这个方法带3个参数,第一个参数是要使用的枚举类型.其语法是关键字typeof后跟放在括号中的枚举类名.第二个参数是要转换的字符 ...
- 使用 Spring Cloud LoadBalancer 实现客户端负载均衡
使用 Spring Cloud LoadBalancer 实现客户端负载均衡 作者:Grey 原文地址: 博客园:使用 Spring Cloud LoadBalancer 实现客户端负载均衡 CSDN ...
- 「工具推荐」golang 代码可视化工具 go-callvis
「工具推荐」go-callvis go-callvis是相对 以图片的形式展示了go程序的调用关系,这个工具在看复杂项目时尤其有用. 亲测,借助它看祖传golang代码,头痛好多了. 安装 go ge ...