深入理解java的finalize

深入理解java的finalize基本预备相关知识 nbsp 1java 的 GC 只负责内存相关的清理 所有其它资源的清理必须由程序员手工完成 要不然会引起资源泄露 有可能导致程序崩溃 nbsp 2 调用 GC 并不保证 GC 实际执行 nbsp 3finalize 抛出的未捕获异常只会导致该对象的 finalize 执行退出 nbsp 4 用户以自己调用对象的 finalize 方法 但是这种调用是正常的方法调用 和对象的销毁过程无关 nbsp 5JVM 保证

基本预备相关知识 

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

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

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

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

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

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

Java代码  
收藏代码

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

对象的销毁过程 

在对象的销毁过程中,按照对象的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 其它的对象。 

来看看对象的状态转换图。 
深入理解java的finalize  
好大,好晕,慢慢看。 

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。 

对象重生的例子 

Java代码  
收藏代码

  1. class C {  
  2.     static A a;  
  3. }  
  4.   
  5. class A {  
  6.     B b;  
  7.   
  8.     public A(B b) {  
  9.         this.b = b;  
  10.     }  
  11.   
  12.     @Override  
  13.     public void finalize() {  
  14.         System.out.println(“A finalize”);  
  15.         C.a = this;  
  16.     }  
  17. }  
  18.   
  19. class B {  
  20.     String name;  
  21.     int age;  
  22.   
  23.     public B(String name, int age) {  
  24.         this.name = name;  
  25.         this.age = age;  
  26.     }  
  27.   
  28.     @Override  
  29.     public void finalize() {  
  30.         System.out.println(“B finalize”);  
  31.     }  
  32.   
  33.     @Override  
  34.     public String toString() {  
  35.         return name + ” is “ + age;  
  36.     }  
  37. }  
  38.   
  39. public class Main {  
  40.     public static void main(String[] args) throws Exception {  
  41.         A a = new A(new B(“allen”20));  
  42.         a = null;  
  43.   
  44.         System.gc();  
  45.         Thread.sleep(5000);  
  46.         System.out.println(C.a.b);  
  47.     }  
  48. }  



期待输出 

Java代码  
收藏代码

  1. A finalize  
  2. B finalize  
  3. allen is 20  


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

对象的finalize的执行顺序 

所有finalizable的对象的finalize的执行是不确定的,既不确定由哪个线程执行,也不确定执行的顺序。 
考虑以下情况就明白为什么了,实例a,b,c是一组相互循环引用的finalizable对象。 

何时及如何使用finalize 

从以上的分析得出,以下结论。 
1 最重要的,尽量不要用finalize,太复杂了,还是让系统照管比较好。可以定义其它的方法来释放非内存资源。 
2 如果用,尽量简单。 
3 如果用,避免对象再生,这个是自己给自己找麻烦。 
4 可以用来保护非内存资源被释放。即使我们定义了其它的方法来释放非内存资源,但是其它人未必会调用该方法来释放。在finalize里面可以检查一下,如果没有释放就释放好了,晚释放总比不释放好。 
5 即使对象的finalize已经运行了,不能保证该对象被销毁。要实现一些保证对象彻底被销毁时的动作,只能依赖于java.lang.ref里面的类和GC交互了。 

参考 

关于引用类型,GC,finalize的相互交互可以参考 ReferenceQueue GC finalize Reference 测试及相关问题























版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233983.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • MySQL8.0.26安装配置教程(windows 64位)

    MySQL8.0.26安装配置教程(windows 64位)一.进入MySQL官网下载安装进入Mysql官网MySQL点击DOWNLOADS下拉页面点击红框内容跳转页面后点击红框下载后并解压到目标文件夹(一定要记住路径)二.配置并初始化MySQL首先创建一个txt文档,并复制如下代码其中安装目录需更改为你所下载的目录存放目录需改为事先新建好的data文件夹目录[mysqld]#设置3306端口port=3306#设置mysql的安装目录basedir=D:\mysql-8.0.26-winx.

    2022年6月9日
    53
  • 震荡波病毒原代码(勒索病毒源代码)

    #include#include#include#include#include#defineNORM”/033[00;00m”#defineGREEN”/033[01;32m”#defineYELL”/033[01;33m”#defineRED”/033[01;31m”#defineBANNERGREEN”[%%]”YELL”mandrag

    2022年4月14日
    79
  • AVX2 初探

    AVX2 初探Intel最近发布了AVX-512,据说对浮点运算有很大提升,我的机器目前不支持AVX-512,但是支持AVX2,按照之前Intel给出的数据,据说能提速将近8倍:IntroductiontoIntel®AdvancedVectorExtensions测试环境可能这篇文章有点偏老,我这边也想验证一下没有优化的C/C++浮点运算和AVX2优化后的浮点运算到底快多少。这是我机器的配置:Manufacturer GenuineIntelName IntelCorei77820

    2022年5月30日
    82
  • vim不能复制粘贴_在筛选状态下怎么复制粘贴

    vim不能复制粘贴_在筛选状态下怎么复制粘贴前言这是一则记录贴,防止小技巧遗忘。不知道大家是否会有这种困扰,例如在AndroidStudio有一段缩进优美的代码实现,例如:publicvoidsayHello(){Stringmsg=”HelloVimPasteMode”;System.out.println(msg);}当你把这段缩进优美的代码直接ctrl+c,ctrl+v到Vim的时候,就会出现如

    2025年11月22日
    4
  • Windows下搭建FTP服务器的一些总结

    Windows下搭建FTP服务器的一些总结Windows下搭建FTP服务器前言:如果你的电脑上的控制面板–程序–打开或关闭windows功能–windows功能里面没有互联网信息服务(IIS),那就别在网上浪费时间了,我找着了一天的资料打了各种补丁没成功,没有相关的IIS或者连c:\windows\system32\inetsrv这个路径都没有,原因是因为你的电脑系统可能是家庭版win7,或者是ghost安装系统的时候精简…

    2022年7月21日
    19
  • java打印数组_Java中打印数组内容的方式有哪些?

    java打印数组_Java中打印数组内容的方式有哪些?下面是几种常见的打印方式。方法一:使用循环打印。publicclassDemo{publicstaticvoidmain(String[]args){String[]infos=newString[]{“Java”,”Android”,”C/C++”,”Kotlin”};StringBufferstrBuffer=newStringBuffer();for(…

    2022年5月2日
    47

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号