• 谈谈你对 finalize 方法的理解,该怎样回答才能体现出高水平?浮于表面的答案永远不行!


    目录

    1. finalize 方法是什么?

    2. finalize 方法的作用?

    3. 为什么强烈不建议在 finalize 方法中进行资源的释放和清理工作


    1. finalize 方法是什么?

    首先要知道,finalize 方法是 Object 类中的一个方法,从源码中叶可儿一看到,所以所有的对象都会具有这个方法,该方法修饰符为 protected ,子类可以进行 @Override 重写,但实际开发过程中一般不会去理会它。

    在 JDK9 之后,该方法已经被官方不推荐使用了,但面试的时候还是有可能会被问到的,所以我们就来聊聊该方法的一些作用。

    2. finalize 方法的作用?

    finalize 方法是在对象被垃圾回收器回收之前执行的,当我们将一个对象设置为 null 之后,它就变成了垃圾,等待着垃圾回收器回收,finalize 方法主要就是做一些清理善后的工作,如果一个对象重写了 finalize 方法,那么它在被垃圾回收器回收之前,就会先执行 finalize 方法。

    由此也带来了它的另一个作用,通常一个对象都是具有很多的功能属性的,所以我们就可以将一些资源的释放;例如文件关闭操作,网络连接断开操作放到 finalize 方法中去完成。

    如下代码所示

    1. public class Test {
    2. // 重写 Test 类中的 finalize() 方法
    3. @Override
    4. public void finalize() throws Throwable{
    5. // 打印一句话作为标记,证明该方法被调用过
    6. System.out.println("finalize方法执行");
    7. }
    8. public static void main(String[] args) throws Exception {
    9. // 创建类对象 t
    10. Test t = new Test();
    11. // 获取该对象
    12. System.out.println(t+"第一次获取对象");
    13. // 将对象t赋值为 null,此时堆中的对象就会变成垃圾
    14. t = null;
    15. // 手动开启垃圾回收GC,将垃圾回收,验证 finalize方法是否执行
    16. System.gc();
    17. // 因为GC垃圾回收是另外的垃圾回收线程,所以我们让主线程先睡两秒,避免造成误差
    18. Thread.sleep(2000);
    19. // 经过GC之后再次获取t对象
    20. System.out.println(t+"第二次获取对象");
    21. }
    22. }

    这里还有一点需要提醒注意,Java虚拟机的GC是单独的GC垃圾回收线程,并且该线程是 main 线程的守护线程,只要 main 线程运行结束,GC垃圾回收线程就会停止,所以在代码中我才让 main 线程调用 sleep 方法。

    然后我们运行上述方法,在控制台中得到如下所示结果

    可以看到,将对象 t 赋值为 null 之后,finalize 方法就执行了。

    3. 为什么强烈不建议在 finalize 方法中进行资源的释放和清理工作

    经过了上面的演示,现在已经得知,finalize 方法会在对象被垃圾回收器回收之前执行此方法,但是!!!强烈不建议在 finalize 方法中进行资源的释放和清理工作。

    原因我总结了以下三点

    (1)资源释放实际成谜。

    如果我们重写了 finalize 方法,那么对象在变成垃圾被垃圾回收器回收之前,需要先执行 finalize 方法,而 finalize 方法并不是由垃圾回收器来调用的,是有一个专门的线程叫 finalizer 来调用的,finalizer 线程内部有一个引用队列,垃圾回收器识别到垃圾对象之后,会把该对象添加到引用队列中去,finalizer 线程会依次调用每个对象的 finalize 方法然后进行清理。如果说一个对象不再被任何一个变量引用,我们就希望它能够马上被回收释放占用的系统资源,但是垃圾回收算法的运行时机是不确定的,而且缺少对该垃圾对象的监控,所以我们无从得知盖拉几何时会被清理掉,资源的释放时机就成了谜。

    (2)资源释放异常,对象变成"僵尸"。

    finalize 既然是一个方法,那么它就有可能抛出异常,如果抛出异常,垃圾清理到一半又没有清理完,此时这个对象就不能被清除掉,就变成半死不活的"僵尸"了,一直占用系统资源无法被释放。

    (3)很容易导致OOM(内存溢出)

    GC垃圾回收线程和主程序的线程不是同一个线程。也就是说,我们的主线程,也可以说是业务一直都在持续地进行着,那么业务就很有可能会在内存中继续创建出新的对象进行其他的业务操作。而此时先前创建的对象被回收需要调用 finalize 方法,如果我们将一些资源的释放工作和内存清理工作全部放到 finalize 方法中的话,是非常耗时的,特别是网络断开连接操作,需要等待对方的响应才能断开连接,但现在内存中的垃圾对象又需要等待 finalize 方法执行完毕后才能清除,由于业务的不断进行,还有不断新的对象被创建,垃圾对象的清理速度慢,新对象的创建速度快,清理的速度没有创建的速度快,一定会造成内存溢出。

  • 相关阅读:
    C语言数组相关问题深度理解
    uniapp 微信小程序分享功能(打开右上角分享)
    cspj2022 T4 上升点列(point)题解(floyd)
    win32com报错:无效的类字符串(Invalid Class String)
    GitHub官方出手,一针见血。Spring Boot趣味实战手册来袭(彩版)
    计算机基本架构-时序逻辑电路回顾
    雨水情自动监测 遥测终端机
    西安健身房共享系统开发如何快速回笼资金?
    Spring IOC - Spring启动过程解析
    C#毕业设计——基于C#+asp.net+sqlserver的学生信息管理系统设计与实现(毕业论文+程序源码)——学生信息管理系统
  • 原文地址:https://blog.csdn.net/m0_70325779/article/details/133268920