深入理解java的finalize、GC、close()的优劣

news/2024/7/3 5:57:17
目录 


基本预备相关知识 
对象的销毁过程 
对象重生的例子 
对象的finalize的执行顺序 
何时及如何使用finalize 
参考 


基本预备相关知识 


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 其它的对象。 


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

 

好大,好晕,慢慢看。 


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交互了。 


参考 
1、作者:zhang_xzhi_xjtu
网址:http://www.iteye.com/topic/484934
2、关于引用类型,GC,finalize的相互交互可以参考ReferenceQueue GC finalize Reference 测试及相关问题


http://www.niftyadmin.cn/n/3649597.html

相关文章

后端开发:SpringBoot实现注册与登录功能

这次实现的注册与登录功能需要进行数据库的基本操作,而且是前后端分离式开发。总的来说就是首先进行数据库的设计,然后根据数据库进行编写服务端API接口,接着来到客户端或移动端,进行登录与注册的界面设计,接收服务端提…

[sync4j]Nokia手机和sync4j服务器同步的第四次手机登录,手工新建了syncSource同步源

[sync4j]Nokia手机和sync4j服务器同步的第四次手机登录:在sync4j社区看到一个话题,讨论如何纠正Nokia系列手机会自动在远程数据库前面添加一个“./”符号。据Harrie说,“You can work around this by configure a similar syncsourceas the o…

debian tomcat_如何在Debian 10上安装Apache Tomcat 9

debian tomcat介绍 (Introduction) Apache Tomcat is a web server and servlet container that is used to serve Java applications. Tomcat is an open source implementation of the Java Servlet and JavaServer Pages technologies, released by the Apache Software Fou…

移动开发:Ionic框架实现注册与登录功能

由于项目是前后端分离式开发,所以移动端使用ionic框架,后端API接口使用SpringBoot框架。注册与登录的后端实现可以参考我的这篇文章:后端开发:SpringBoot实现注册与登录功能。ionic框架实现注册与登录其实就是调用后端API接口对数…

Android根据分辨率进行单位转换-(dp,sp转像素px) - topMan'blog - ITeye技术网站

【转】Android根据分辨率进行单位转换-(dp,sp转像素px) 博客分类: Android 开发学习 Android系统中,默认的单位是像素(px)。也就是说,在没有明确说明的情况下,所有的大小设置都是以像素为单位。 如果以像素设置大小,会…

[sync4j]Nokia手机和sync4j服务器同步的第三次尝试

第三次手机登录:按照前面所说的,设置手机上面的“远程数据库”为“./contact”,然后做手机同步。结果,经过漫长的初始化时间,手机上报告错误“连接错误同步类型不被支持无法和服务器同步”在服务器日志中,我…

Ionic6使用组件出现错误:Did you add it to @NgModule.entryComponents

缘由 在Ionic6和Angular8项目中使用组件时出现错误:Error: No component factory found for LoginComponent. Did you add it to NgModule.entryComponents? 在我的上一篇文章:移动开发:Ionic框架实现注册与登录功能中,实现软件运行时弹出登…

new一个Object对象占用多少内存?

Java的自动内存管理机制省却了很多编码工作,大大地提高了Java的生产力,而且JVM的性能也越来越好,特别是G1的出现,改善了垃圾回收中stop the world的状况。 也许很多人都没有考虑过这个问题,new一个Object对象到底占用多…