基本预备相关知识

1 java的GC只负责内存相关的清理,所有其它资源的清理必须由程序员手工完成。要不然会引起资源泄露,有可能导致程序崩溃。

2 调用GC并不保证GC实际执行。

3 finalize抛出的未捕获异常只会导致该对象的finalize执行退出。

4 用户可以自己调用对象的finalize方法,但是这种调用是正常的方法调用,和对象的销毁过程无关。

5 JVM保证在一个对象所占用的内存被回收之前,如果它实现了finalize方法,则该方法一定会被调用。Object的默认finalize什么都不做,为了效率,GC可以认为一个什么都不做的finalize不存在。

6 对象的finalize调用链和clone调用链一样,必须手工构造

protected void finalize() throws Throwable {
super.finalize();
}

对象的销毁过程

在对象的销毁过程中,按照对象的finalize的执行情况,可以分为以下几种,系统会记录对象的对应状态:

unfinalized 没有执行finalize,系统也不准备执行。

finalizable 可以执行finalize了,系统会在随后的某个时间执行finalize。

finalized 该对象的finalize已经被执行了。

GC怎么来保持对finalizable的对象的追踪呢。GC有一个Queue,叫做F-Queue,所有对象在变为finalizable的时候会加入到该Queue,然后等待GC执行它的finalize方法。

这时我们引入了对对象的另外一种记录分类,系统可以检查到一个对象属于哪一种。

reachable 从活动的对象引用链可以到达的对象。包括所有线程当前栈的局部变量,所有的静态变量等等。

finalizer-reachable 除了reachable外,从F-Queue可以通过引用到达的对象。

unreachable 其它的对象。

来看看对象的状态转换图。

1 首先,所有的对象都是从Reachable+Unfinalized走向死亡之路的。

2 当从当前活动集到对象不可达时,对象可以从Reachable状态变到F-Reachable或者Unreachable状态。

3 当对象为非Reachable+Unfinalized时,GC会把它移入F-Queue,状态变为F-Reachable+Finalizable。

4
好了,关键的来了,任何时候,GC都可以从F-Queue中拿到一个Finalizable的对象,标记它为Finalized,然后执行它的
finalize方法,由于该对象在这个线程中又可达了,于是该对象变成Reachable了(并且Finalized)。而finalize方法执行
时,又有可能把其它的F-Reachable的对象变为一个Reachable的,这个叫做对象再生。

5
当一个对象在Unreachable+Unfinalized时,如果该对象使用的是默认的Object的finalize,或者虽然重写了,但是新的实
现什么也不干。为了性能,GC可以把该对象之间变到Reclaimed状态直接销毁,而不用加入到F-Queue等待GC做进一步处理。

6 从状态图看出,不管怎么折腾,任意一个对象的finalize只至多执行一次,一旦对象变为Finalized,就怎么也不会在回到F-Queue去了。当然没有机会再执行finalize了。

7 当对象处于Unreachable+Finalized时,该对象离真正的死亡不远了。GC可以安全的回收该对象的内存了。进入Reclaimed。

对象重生的例子

    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 finalize  
B finalize  
allen is 20

但是有可能失败,源于GC的不确定性以及时序问题,多跑几次应该可以有成功的。详细解释见文末的参考文档。

对象的finalize的执行顺序

所有finalizable的对象的finalize的执行是不确定的,既不确定由哪个线程执行,也不确定执行的顺序。

考虑以下情况就明白为什么了,实例a,b,c是一组相互循环引用的finalizable对象。

何时及如何使用finalize

从以上的分析得出,以下结论。

1 最重要的,尽量不要用finalize,太复杂了,还是让系统照管比较好。可以定义其它的方法来释放非内存资源。

2 如果用,尽量简单。

3 如果用,避免对象再生,这个是自己给自己找麻烦。

4 可以用来保护非内存资源被释放。即使我们定义了其它的方法来释放非内存资源,但是其它人未必会调用该方法来释放。在finalize里面可以检查一下,如果没有释放就释放好了,晚释放总比不释放好。

5 即使对象的finalize已经运行了,不能保证该对象被销毁。要实现一些保证对象彻底被销毁时的动作,只能依赖于java.lang.ref里面的类和GC交互了

理解java的finalize的更多相关文章

  1. java的finalize方法使用

    1. finalize的作用 finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法. finalize()与C++中的析构函数 ...

  2. 《深入理解Java虚拟机》虚拟机性能监控与故障处理工具

    上节学习回顾 从课本章节划分,<垃圾收集器>和<内存分配策略>这两篇随笔同属一章节,主要是从理论+实验的手段来讲解JVM的内存处理机制.好让我们对JVM运行机制有一个良好的概念 ...

  3. 深入理解Java虚拟机--下

    深入理解Java虚拟机--下 参考:https://www.zybuluo.com/jewes/note/57352 第10章 早期(编译期)优化 10.1 概述 Java语言的"编译期&q ...

  4. 深入理解Java虚拟机--上

    深入理解Java虚拟机--上 第2章 Java内存区域和内存溢出异常 2.2 运行时数据区域 图 2-1 Java虚拟机运行时数据区 2.2.1 程序计数器 程序计数器可以看作是当前线程所执行的字节码 ...

  5. 《深入理解JAVA虚拟机》笔记1

    java程序运行时的内存空间,按照虚拟机规范有下面几项: )程序计数器 指示下条命令执行地址.当然是线程私有,不然线程怎么能并行的起来. 不重要,占内存很小,忽略不计. )方法区 这个名字很让我迷惑. ...

  6. 深入理解java虚拟机之垃圾收集器

    Java一个重要的优势就是通过垃圾管理器GC (Garbage Collection)自动管理和回收内存,程序员无需通过调用方法来释放内存.也因此很好多的程序员可能会认为Java程序不会出现内存泄漏的 ...

  7. 深入理解Java虚拟机之JVM垃圾回收随笔

    1.对象已经死亡? 1.1引用计数法:给对象中添加一个引用计数器,每当有一个地方引用他时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器都为0的对象就是不可能再被使用 的.但是它很难解决 ...

  8. 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...

  9. 全面理解Java内存模型(JMM)及volatile关键字(转载)

    关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...

随机推荐

  1. 一个统计报表sql问题

    今天朋友问我了一个统计报表的sql问题.我弄了好久.终于算是写出来了.这里记录下. 问题:  id--------------name--------------addtime1            ...

  2. thinkphp5命名规范

    类的命名采用驼峰法,并且首字母大写.如:User.UserType[不需要加后缀.如IndexController是没必要的,应当直接为Index.接口或者抽象类也保持这个规范] 属性命名采用驼峰法, ...

  3. Photo Shop 修改、维护

    调整画布大小 要继续放更多的图片? 更改画布大小 移动图标 若图标为独立图层,则用移动工具拖动即可 若图层为非独立图层 - 用选区工具选中图标区域 - 用移动工具拖动图标 如果要拆分同一图层下的两个图 ...

  4. PHP判断访问者手机移动端还是PC端的函数,亲测好用

    ,用手机访问PC端WWW域名的时候,自动判断跳转到移动端,用电脑访问M域名手机网站的时候,自动跳转到PC端,我们团队在开发erdaicms二代旅游CMS网站管理系统的时候(http://www.erd ...

  5. JavaScript(二)——语法

    1.基本数据类型: 字符串.小数.整数.日期时间.布尔型等. 2.类型转换: 分为自动转换和强制转换,一般用强制转换. 其他类型转换为整数:parseint(): 其他类型转换为小数:parseflo ...

  6. Spring 整体架构

    1. Core Container:核心容器(core.Beans.Context.Expression Language   Core.Beans框架基础构成,提供IOC.依赖注入特性.BeanFa ...

  7. Win7家庭组的使用

    Win7中有两种方式与他人共享文件,一种是“家庭组”,另一种是使用“工作组或域”. 家庭组是Win7中新增的一种共享文件方式,在传统的共享文件方式中必须手动设置所要共享的文件夹,而家庭组则不需要,只需 ...

  8. RabbitMQ入门教程——.NET客户端使用

    众所周知RabbitMQ使用的是AMQP协议.我们知道AMQP是一种网络协议,能够支持符合要求的客户端应用和消息中间件代理之间进行通信. 其中消息代理扮演的角色就是从生产者那儿接受消息,并根据既定的路 ...

  9. MySQL 调优基础(五) Linux网络

    1. TCP/IP模型 我们一般知道OSI的网络参考模型是分为7层:“应表会传网数物”——应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.而实际的Linux网络层协议是参照了OSI标准,但 ...

  10. openstack-kilo--issue(九) heat stacks topology中图形无法正常显示

    ======声明======= 欢迎转载:转载请注明出处 http://www.cnblogs.com/horizonli/p/6186581.html ==========环境=========== ...