java的finalize()函数
在说明finalize()的用法之前要树立有关于java垃圾回收器几个观点:
- "对象可以不被垃圾回收" : java的垃圾回收遵循一个特点, 就是能不回收就不会回收.只要程序的内存没有达到即将用完的地步, 对象占用的空间就不会被释放.因为如果程序正常结束了,而且垃圾回收器没有释放申请的内存, 那么随着程序的正常退出, 申请的内存会自动交还给操作系统; 而且垃圾回收本身就需要付出代价, 是有一定开销的, 如果不使用,就不会存在这一部分的开销.
- 垃圾回收只能回收内存, 而且只能回收内存中由java创建对象方式(堆)创建的对象所占用的那一部分内存, 无法回收其他资源, 比如文件操作的句柄, 数据库的连接等等.
- 垃圾回收不是C++中的析构. 两者不是对应关系, 因为第一点就指出了垃圾回收的发生是不确定的, 而C++中析构函数是由程序员控制(delete) 或者离开器作用域时自动调用发生, 是在确定的时间对对象进行销毁并释放其所占用的内存.
- 调用垃圾回收器(GC)不一定保证垃圾回收器的运行
finalize()的功能 : 一旦垃圾回收器准备释放对象所占的内存空间, 如果对象覆盖了finalize()并且函数体内不能是空的, 就会首先调用对象的finalize(), 然后在下一次垃圾回收动作发生的时候真正收回对象所占的空间.
finalize()有一个特点就是: JVM始终只调用一次. 无论这个对象被垃圾回收器标记为什么状态, finalize()始终只调用一次. 但是程序员在代码中主动调用的不记录在这之内.
如果需要释放对象的父类所占的资源, 那么就必须自己手动构造finalize调用链
protected void finalize() throws Throwable
{
super.finalize();
}
finalize()主要使用的方面:
- 根据垃圾回收器的第2点可知, java垃圾回收器只能回收创建在堆中的java对象, 而对于不是这种方式创建的对象则没有方法处理, 这就需要使用finalize()对这部分对象所占的资源进行释放. 使用到这一点的就是JNI本地对象, 通过JNI来调用本地方法创建的对象只能通过finalize()保证使用之后进行销毁,释放内存
- 充当保证使用之后释放资源的最后一道屏障, 比如使用数据库连接之后未断开,并且由于程序员的个人原因忘记了释放连接, 这时就只能依靠finalize()函数来释放资源.
- 《thinking in java》中所讲到的“终结条件”验证, 通过finalize()方法来试图找出程序的漏洞
尽管finalize()可以主动调用, 但是最好不要主动调用, 因为在代码中主动调用之后, 如果JVM再次调用, 由于之前的调用已经释放过资源了,所以二次释放资源就有可能出现导致出现空指针等异常, 而恰好这些异常是没有被捕获的, 那么就造成对象处于被破坏的状态, 导致该对象所占用的某一部分资源无法被回收而浪费.
尽量避免使用finalize():
- finalize()不一定会被调用, 因为java的垃圾回收器的特性就决定了它不一定会被调用
- 就算finalize()函数被调用, 它被调用的时间充满了不确定性, 因为程序中其他线程的优先级远远高于执行finalize()函数线程的优先级。也许等到finalize()被调用, 数据库的连接池或者文件句柄早就耗尽了.
- 如果一种未被捕获的异常在使用finalize方法时被抛出,这个异常不会被捕获,finalize方法的终结过程也会终止,造成对象出于破坏的状态。被破坏的对象又很可能导致部分资源无法被回收, 造成浪费.
- finalize()和垃圾回收器的运行本身就要耗费资源, 也许会导致程序的暂时停止.
finalize执行的生命周期:
- finalize流程:当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。
- 对象可由两种状态组成,涉及到两类状态空间,一是终结状态空间 F = {unfinalized, finalizable, finalized};二是可达状态空间 R = {reachable, finalizer-reachable, unreachable}。
- unfinalized : GC未调用对象的finalize(), 也不准备调用对象的finalize().
- finalizable: 表示GC可调用对象的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的,由第4点可知,将对象finalizable对象标记为finalized时会由某个线程执行该对象的finalize方法,致使其变成reachable。这也是图中只有八个状态点的原因
- 程序员手动调用finalize方法并不会影响到上述内部标记的变化,因此JVM只会至多调用finalize一次,即使该对象“复活”也是如此。程序员手动调用多少次不影响JVM的行为
- 若JVM检测到finalized状态的对象变成unreachable,回收其内存(I)
- 若对象并未覆盖finalize方法,JVM会进行优化,直接回收对象(O)
注:System.runFinalizersOnExit()等方法可以使对象即使处于reachable状态,JVM仍对其执行finalize方法
对象重生的代码1:
class C {
static A a;
}
class A {
B b;
public A(B b) {
this.b = b;
}
@Override
public void finalize() {
System.out.println("A finalize");
C.a = this;
}
}
class B {
String name;
int age;
public B(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void finalize() {
System.out.println("B finalize");
}
@Override
public String toString() {
return name + " is " + age;
}
}
public class Main {
public static void main(String[] args) throws Exception {
A a = new A(new B("allen", 20));
a = null;
System.gc();
Thread.sleep(5000);
System.out.println(C.a.b);
}
}
我的理解:为方便起见, 把a,b两个变量所指的内存空间就叫做a和b
- A a = new A(new B("allen" , 20)) ; //此时a和b都是reachable, unfinalized状态
- a = null ;
- 这之后, a和b的状态会在某一个时刻变成unreachable, unfinalized(但是b变成了unreachable还是f-reachable我不是很确定, 如果大家知道,欢迎补充^_^)
- 或者a和b直接变成f-reachable, unfianlized.
- 然后在某个时刻,GC检测到a和b处于unfinalized状态, 就将他们添加到F-queue,并将状态改为f-reachable finalizable.
- 之后分两种情况:
- 第一: GC从F-queue中首先取出a, 并被某个线程执行了finalize(), 也就相当于被某个活动的线程持有, a状态变成了reachable, finalized. 此时由于a被c对象所引用,所以之后不会变成unreachable finalized而被销毁(重生) 与此同时, b由于一直被a所引用, 所以b的状态变成了reachable, finalizable. 然后在某个时刻被从F-queue取出, 变成reachable, finalized状态
- 第二: GC从F-queue中首先取出b,并被某个线程执行了finalize(), 状态变成reachable finalized. 然后a也类似, 变成reachable finalized状态, 并被c引用, 重生
对象重生的代码2:
public class GC
{
public static GC SAVE_HOOK = null; public static void main(String[] args) throws InterruptedException, Throwable
{
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;
}
}
//资料来自: http://blog.csdn.net/rsljdkt/article/details/12242007# ; http://blog.sina.com.cn/s/blog_66a6172c01018jda.html ; http://zhang-xzhi-xjtu.iteye.com/blog/484934
//《thingking in java》笔记。 如果有什么不对的地方, 欢迎指正^_^
java的finalize()函数的更多相关文章
- AJPFX浅谈Java性能优化之finalize 函数
★finalize 函数的调用机制 俺经常啰嗦:“了解本质机制的重要性”.所以今天也得先谈谈 finalize 函数的调用机制.在聊之前,先声明一下:Java虚拟机规范,并没有硬性规定垃圾回收该不该搞 ...
- java的finalize()方法与C++的析构函数
---<java编程思想> 读书笔记 --- 2017/3/15 读<java编程思想>读到初始化与清理一章,文中提及java的finalize()方法,联想到了C++的析构函 ...
- Java中finalize()用法
Java中finalize() 垃圾回收器要回收对象的时候,首先要调用这个类的finalize方法(你可以 写程序验证这个结论),一般的纯Java编写的Class不需要重新覆盖这个方法,因为Obj ...
- java String.split()函数的用法分析
java String.split()函数的用法分析 栏目:Java基础 作者:admin 日期:2015-04-06 评论:0 点击: 3,195 次 在java.lang包中有String.spl ...
- Android使用JNI(从java调用本地函数)
当编写一个混合有本地C代码和Java的应用程序时,需要使用Java本地接口(JNI)作为连接桥梁.JNI作为一个软件层和API,允许使用本地代码调用Java对象的方法,同时也允许在Java方法中调用本 ...
- 关于C++与Java中虚函数问题的读书笔记
之前一直用C++编程,对虚函数还是一些较为肤浅的理解.可近期由于某些原因搞了下Java,发现有些知识点不熟,于是站在先驱巨人的肩上谈谈C++与Java中虚函数问题. Java中的虚函数 以下是段别人的 ...
- Java中finalize方法用途何在?
package thinking.in.java.demo; /* * finalize的用途何在? * *本例的终止条件是L所有的Book对象在被当做垃圾回收前都应该被签入.但是在main方法中 * ...
- 用JAVA写一个函数,功能例如以下: 随意给定一组数, 找出随意数相加之后的结果为35(随意设定)的情况
用JAVA写一个函数.功能例如以下:随意给定一组数,比如{12,60,-8,99,15,35,17,18},找出随意数相加之后的结果为35(随意设定)的情况. 能够递归算法来解: package te ...
- java中main函数解析(转载)
从写java至今,写的最多的可能就是主函数 public static void main(String[] args) {} 但是以前一直都没有问自己,为什么要这么写,因为在c语言中就没有这样子的要 ...
随机推荐
- MySQL使用二进制日志恢复数据库
一.二进制日志简介 MySQL有不同类型的日志,其中二进制文件记录了所有对数据库的修改,如果数据库因为操作不当或其他原因丢失了数据,可以通过二进制文件恢复. 在my.ini文件中设置了log-bin, ...
- 11.21 if条件语句 年月日执行判断
<script language="javascript"> var nian=prompt("输入一个年份","") if(n ...
- Struts2之Struts2-2.5.5 Interceptor
Struts2-2.5.5版本是目前为止最新的版本了,相对于之前的2.3版本以及再之前的版本而言,新版本改动了很多. 好了,废话不多说,GO CODE! 基本jar包: web.xml核心配置,这里要 ...
- Mdrill 安装部署(单机版)
前期: -------------------------------------------------------- 准备: 1.centos6.5 /7 x86_64(后期会更改主机名称) 2. ...
- 七个结构模式之适配器模式(Adapter Pattern)
定义: 将一个接口转换为客户需要的另外一个接口,使接口不兼容的类型可以一起工作,也被称为包装器模式(Wrapper Patern). 结构图: Target:目标抽象类,客户所需要的接口. Adapt ...
- linux常用命令之查阅文件
CAT cat – concatenate print files 连续的输出文件内容 用法 cat [-nbA] file 选项 -n line number 输出行号 -b line number ...
- Unsupported configuration attributes: [FILE_UPLOAD]
Caused by: java.lang.IllegalArgumentException: Unsupported configuration attributes: [FILE_UPLOAD] 情 ...
- Sharepoint2013:日期控件报错
1 问题 1> 在sharepoint中的,日期控件(DateTimeControl),日期过滤控件(Date Filter)和SPListViewFilter等包含日期组件的控件,在特定情况下 ...
- 【特别推荐】10款唯美浪漫的婚礼 & 结婚纪念网站模板
互联网的重要性不言而喻,如今我们的生活已经完全离不开网络.这里给大家分享一组唯美浪漫的结婚邀请网站以及婚礼请柬网站模板,如果你也正想制作这样的网站,相信这些漂亮的网站模板能够带给你很大的帮助,让你快速 ...
- Sap 常用Function 说明
函数名 描述 SD_VBAP_READ_WITH_VBELN 根据销售订单读取表vbap中的信息EDIT_LINES 把READ_TEXT返回的LINES中的行按照TDFORMAT=“*”重新组织VI ...