finalize()方法是Object类中定义的protect方法。每一个类都可以重写该方法,给出自己的实现。当类在被回收期间,这个方法就可能会被调用到。

为什么说可能?
这是由于finalize()的调用时机甚至是否会被调用到都存在着太多的不确定性。基于这个原因,几乎所有的技术书籍及文章都不推荐开发人员依赖重写finalize()方法来做什么事情。反而是建议开发人员写一个类似析构函数的方法,在对象调用完毕后,手动的执行自己添加的这个方法。对于软件开发这种有时需要非常精确掌控进度的工作,单纯的依赖GC和finalize()方法来控制,非常的困难。
finalize()方法是Java诞生初期,为了推广Java语言,兼容C++使用语法,而做出的让步。对编程稍有了解的人都应该知道C/C++语法都是在结束对象生命周期时,手动的调用析构函数。然而Java等拥有GC机制的语言并不会实时的清理内存,而是在内存分配出现紧张的(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )情况下才会回收。这就导致了回收时机的不确定性,也是Java的finalize()方法和C/C++的析构函数有本质区别的地方。
在Java中,对象的生命周期基本是这样的:
1、类文件编程成功后,编译器会判断当前类是否重写了finalize()方法。如果已经重写了,那么会给当前类打上一个标识:has_finalizer
2、对象在JVM创建后,会根据这个标识,同时再创建一个Finalizer对象。Finalizer对象会持有当前对象的引用。接着JVM会将Finalizer对象放置到一个容器(Finalizer.unfinalized)中。这个容器中的所有的Finalizer对象所持有的对象都没有被GC执行过重写的finalize()方法。
3、在对象根据可达性分析判定需要被回收时,GC会从容器(Finalizer.unfinalized)中获得到回收对象所对应的Finalizer对象,并放置到另外一个对列中:F-QUEUE
4、在GC机制中,有一个低优先级的守护线程:FinalizerThread。这个线程是专门用来执行finlize()方法的线程。它会从F-QUEUE中依次的获取Finalizer对象,然后执行Finalizer对象所对应的回收对象重写的finalize()方法。
注意由于Finalizer对象是持有回收对象的引用的,因此在finalize()方法的执行过程中,是可以重新设置对象的引用到一个不会被回收的对象的属性上的,最终阻止当前对象被回收的。
5、在finalize()方法被执行后,对应的Finalizer对象会被从最初的容器(Finalizer.unfinalized)移除掉。
6、由于对象可能在第四步中重新被其他对象持有,因此需要重新确认一下这些对象。这时候GC会重新扫描一下F-QUEUE中所对应的对象。把仍然需要回收的对象回收掉。
这里有一下情况需要注意:
(1)由于容器(Finalizer.unfinalized)已经在第五步中移除掉了Finalizer对象,因此未来对象仍要被回收时,是不会再被调用到finalize()方法的。也就是说一个对象的finalize()方法只会被回收一次,无论这个对象是否是回收后又“重生”的。
(2)finalize()方法是单线程串行回收的,所以如果finalize()方法耗时或者死循环什么的就会影响其它对象的finalize()方法执行(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ ),因此也可以看出来finalize()方法的执行非常不确定。
(3)finalize()方法如果没有执行完,尽管当前对象已经不被GC-ROOT持有,但是仍然不会被回收掉。这就导致了内存的泄露,未来可能会出现内存溢出。
这里可以用如下的方法测试:

 1 public class GCFinalizer
2 {
3 String name;
4 String[] kStrings = new String[1024*64];
5
6 public static void main(String[] args) throws InterruptedException
7 {
8 boolean isClear = true;
9 for (int i = 0; i < 10000; i++)
10 {
11 System.out.println(isClear + "Name" + (i - 1));
12 GCFinalizer gcf = new GCFinalizer();
13 gcf.name = "Name" + i;
14 gcf = null;
15 System.gc();
16 Thread.sleep(500);
17 }
18 }
19
20 protected void finalize() throws InterruptedException
21 {
22 while (true)
23 {
24 System.out.println(name);
25 Thread.sleep(1000);
26 }
27 }
28 }

结果如下图

使用内存监视工具查看到的内存曲线:

(4)如果A对象持有B对象,A引用被外界断开。但是A复写了finalize()方法,方法中A对象被GC—ROOT对象再次持有,那么这时候B是否已经被回收掉了呢?
这里可以用如下的方法测试:

 1 public class GCF
2 {
3 public String name;
4
5 public GCF(String name)
6 {
7 this.name = name;
8 }
9
10 public GCF subGCF;
11
12 static GCF ref;
13
14 public static void main(String[] args) throws InterruptedException
15 {
16 GCF gcf = new GCF("masterName");
17 gcf.subGCF = new GCF("subName");
18 gcf = null;
19 System.gc();
20 Thread.sleep(3000);
21 gcf = ref;
22 System.out.println("step1");
23 System.out.println("is Sub GCF Obj exist:" + (gcf.subGCF != null));
24 gcf = null;
25 ref = null;
26 System.gc();
27 Thread.sleep(1000);
28 System.out.println("step2");
29 gcf = ref;
30 System.out.println("is reborn:" + (gcf != null));
31 while (true)
32 {
33 Thread.sleep(1000);
34 }
35 }
36
37 @Override
38 protected void finalize() throws InterruptedException
39 {
40 System.out.println(name);
41 if (name.equals("masterName"))
42 {
43 ref = this;
44 }
45 }
46 }

通过返回结果我们可以知道,在A对象被重新持有以(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )后,B对象没有被回收掉:
更准确的说是:B对象发生了回收,但是仍然可以通过A对象的属性再次访问到

JVM GC-----4、finalize()方法的更多相关文章

  1. JAVA中GC时finalize()方法是不是一定会被执行?

    在回答上面问题之前,我们一定要了解JVM在进行垃圾回收时的机制,首先: 一.可达性算法  要知道对象什么时候死亡,我们需要先知道JVM的GC是如何判断对象是可以回收的.JAVA是通过可达性算法来来判断 ...

  2. 禁止使用finalize方法

    Don´t use Finalizers, mainly because are unpredictable and we don´t know when will be executed, &quo ...

  3. GC执行finalize的过程以及对象的一次自我拯救

    参考资料:深入理解java虚拟机 /** * 此代码演示了两点: * 1.对象可以在被GC时自我拯救 * 2.这种自救的机会只有一次,因为一个对象的finalize()方法只会被系统自动调一次 */ ...

  4. JVM GC之对象生死

    1.简述 在Java内存运行时区域的各个部分中,程序计数器.虚拟机栈.本地方法栈3个区域随着线程而生,随着线程而亡.栈中的栈帧随着方法的进入和退出而有条不紊的进行着入栈和出栈操作. 每个栈帧需要分配多 ...

  5. Java GC机制和对象Finalize方法的一点总结

    GC是垃圾收集的意思(Garbage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超 ...

  6. java finalize方法总结、GC执行finalize的过程

    注:本文的目的并不是鼓励使用finalize方法,而是大致理清其作用.问题以及GC执行finalize的过程. 1. finalize的作用 finalize()是Object的protected方法 ...

  7. JVM 基础:回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析

    转自:https://blog.csdn.net/tjiyu/article/details/53982412 1-1.为什么需要了解垃圾回收 目前内存的动态分配与内存回收技术已经相当成熟,但为什么还 ...

  8. 从GC的SuppressFinalize方法带你深刻认识Finalize底层运行机制

    如果你经常看开源项目的源码,你会发现很多Dispose方法中都有这么一句代码: GC.SuppressFinalize(this); ,看过一两次可能无所谓,看多了就来了兴趣,这篇就跟大家聊一聊. 一 ...

  9. 【C#】GC和析构函数(Finalize 方法)

    析构函数: (来自百度百科)析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数.析构函数往往用来做"清理善后&quo ...

随机推荐

  1. Web开发之404小结

    404算是Web工程里最常见的错误代号了.今天做一个小结: 场景:[Tomcat运行正常,但无法访问自己建的项目:404] 结果:在URL拼写正确的情况下,无法访问目标工程任何页面 信息:[404]: ...

  2. transform 图标旋转,IE8、IE7不兼容

    要将图标旋转,只需使用transform的rotate以及transition即可完成旋转的动画效果.ease 规定慢速开始,然后变快,然后慢速结束的过渡效果;   ease-in 规定以慢速开始的过 ...

  3. Dubbo配置参数的优先级

    总结为: 1).Java运行时虚拟机参数 eg:-Ddubbo.protocol.port=20880 2).dubbo.xml || application.properties(SpringBoo ...

  4. SpringBoot几个重要的事件回调、监听机制

    (1).需要配置在META-INF/Spring.factories 1.ApplicationContextInitializer // // Source code recreated from ...

  5. 【CTF WEB】ISCC 2016 web 2题记录

      偶然看到的比赛,我等渣渣跟风做两题,剩下的题目工作太忙没有时间继续做. 第1题 sql注入: 题目知识 考察sql注入知识,题目地址:http://101.200.145.44/web1//ind ...

  6. L-BFGS算法(转载)

    转载链接:http://blog.csdn.net/itplus/article/details/21897715 前面的拟牛顿法.DFP.BFGS.L-BFGS算法简短总结一下就是: 牛顿法不仅使用 ...

  7. CentOS挂载光盘

    mkdir /mnt/cdrom mount /dev/cdrom /mnt/cdrom umount /dev/cdrom /mnt/cdrom 在Ambari集群中配置192.168.0.210: ...

  8. 经典]Linux内核中ioremap映射的透彻理解【转】

    转自:http://blog.csdn.net/lanyang123456/article/details/7403514 几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器.状态寄 ...

  9. linux 下程序员专用搜索源码用来替代grep的软件ack(后来发现一个更快的: ag), 且有vim插件的

    发现一个比ack更快更好用的:  https://github.com/ggreer/the_silver_searcher   , 使用时命令为ag,它是基于ack的代码二次开发的,所有使用方法基本 ...

  10. 【转】wpf中的xmlns命名空间为什么是一个网址,代表了什么意思

    wpf中的xmlns命名空间为什么是一个网址,代表了什么意思 http://blog.csdn.net/catshitone/article/details/71213371