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

目录 


基本预备相关知识 
对象的销毁过程 
对象重生的例子 
对象的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 测试及相关问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: `finalize()` 方法是 Java 中的一个特殊方法,它在对象即将被垃圾回收之前被调用。该方法的定义如下: ```java protected void finalize() throws Throwable { // 执行一些清理操作 } ``` `finalize()` 方法通常用于执行一些清理操作,比如释放资源或关闭文件等。当对象被垃圾回收时,JVM 会自动调用其 `finalize()` 方法,以便执行这些清理操作。 需要注意的是,由于 `finalize()` 方法是在垃圾回收之前被调用的,因此不能保证它会被及时地执行。此外,使用 `finalize()` 方法进行清理操作可能会影响垃圾回收的性能,因为垃圾回收器需要等待 `finalize()` 方法的执行完成才能继续进行下一步操作。 因此,在实现 `finalize()` 方法时,应该尽量避免使用它进行关键性的清理操作,而是应该使用 try-with-resources 或者手动关闭资源等方式来保证资源的正确释放。 ### 回答2: Java中的finalize()方法是一个Object类中的方法,用于在垃圾回收器回收对象之前执行一些清理操作。当一个对象不再被引用时,垃圾回收器会将其标记为可回收状态,并在一段时间后自动回收。 finalize()方法在垃圾回收器回收对象之前被调用,可以用来执行一些对象的清理工作,例如关闭文件、释放资源等。该方法由垃圾回收器自动调用,开发者无法手动执行。 finalize()方法的使用需要慎重考虑,因为它有一些不确定的特性。首先,垃圾回收器的调用时间是不确定的,因此无法确定finalize()方法何时会被执行。其次,一旦finalize()方法被调用,虽然对象已经被标记为可回收,但并不一定会立即被回收。 由于finalize()方法的不确定性和性能问题,Java 9中已经不推荐使用该方法。取而代之的是,开发者应该使用try-with-resources或者手动关闭资源来完成清理工作。try-with-resources是一种自动关闭资源的语法,可以确保在使用完资源后自动释放,避免了由于finalize()方法带来的不确定性和性能问题。 总之,finalize()方法是Java中用于在对象被回收前执行清理操作的方法,但由于其不确定性和性能问题,已经不再推荐使用。开发者应该使用更可靠的方式来执行对象的清理工作。 ### 回答3: Java中的finalize()方法是一个被垃圾回收器在对象回收前调用的方法。垃圾回收器在回收对象时会首先检查该对象是否覆盖了finalize()方法,如果覆盖了,垃圾回收器会在回收对象前调用该方法来执行一些终结操作。 finalize()方法是Object类提供的一个受保护的方法,子类可以选择性地覆盖该方法,以完成对象在被回收前清理资源的工作。当对象变得不可达时,垃圾回收器自动将其标记为可回收对象,并在将其回收之前调用finalize()方法。 覆盖finalize()方法时需要注意几点。首先,finalize()方法在被调用时,可能会被任何线程调用,所以需要考虑到多线程环境下的同步问题。其次,finalize()方法的执行时间是不可预测的,所以不应该在该方法中执行过长或复杂的操作,否则可能导致垃圾回收暂停或延迟。 在实际编程中,应尽量避免使用finalize()方法,因为它的调用时机不可控,不稳定且效率低下。更好的做法是使用try-finally块或使用其他资源管理机制(如使用try-with-resources语句)来确保对象的资源能够得到正确释放和关闭。 总之,finalize()方法是Java中用于对象清理和资源释放的一种机制,但由于其不稳定性和低效性,应尽量避免使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值