深入了解Java中finalize方法的作用和底层原理

目录
  • finalize方法是什么
  • finalize方法与C++的析构函数的区别
    • finalize方法合适清理的对象
    • 可以触发finalize执行的方法
    • finalize实现对象再生问题
  • finalize的执行过程(生命周期)
  • 对象对于finalize方法的两种状态
    • 终结状态空间
    • 可达状态空间
  • 代码示例
    • 对象复活
    • 覆盖finalize方法以确保资源释放

finalize方法是什么

finalize方法是Object的protected方法,Object的子类们可以覆盖该方法以实现资源清理工作,GC在首次回收对象之前调用该方法。

finalize方法与C++的析构函数的区别

finalize方法与C++中的析构函数不是对应的,C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),但Java中的finalize的调用具有不确定性,不建议用finalize方法完成“非内存资源”的清理工作。

finalize方法合适清理的对象

  • 清理本地对象(通过JNI创建的对象);
  • 作为确保某些非内存资源(如Socket、文件等)释放的一个补充,在finalize方法中显式调用其他资源释放方法。

可以触发finalize执行的方法

在Java中含有一些一些与finalize相关的方法,由于一些致命的缺陷,已经被废弃了,如System.runFinalizersOnExit() 方法、Runtime.runFinalizersOnExit() 方法、
System.gc() 与System.runFinalization() 方法。

他们增加了finalize方法执行的机会,但不可盲目依赖它们Java语言规范并不保证finalize方法会被及时地执行、而且根本不会保证它们会被执行finalize方法可能会带来性能问题。

因为JVM通常在单独的低优先级线程中完成finalize的执行。

finalize实现对象再生问题

finalize方法的实现中,可将待回收对象赋值给GC Roots可达的对象引用,从而达到对象再生的目的。

finalize方法至多由GC执行一次(用户当然可以手动调用对象的finalize方法,但并不影响GC对finalize的行为)。

finalize的执行过程(生命周期)

大致描述一下finalize的运行流程:当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。

若对象未执行过finalize方法,将其放入F-Queue队列,由低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。

对象对于finalize方法的两种状态

对象可由两种状态,涉及到两类状态空间,一是终结状态空间 F = {unfinalized, finalizable, finalized};二是可达状态空间 R = {reachable, finalizer-reachable, unreachable}。

终结状态空间

各状态含义如下:

  • unfinalized: 新建对象会先进入此状态,GC并未准备执行其finalize方法,因为该对象是可达的。
  • finalizable: 表示GC可对该对象执行finalize方法,GC已检测到该对象不可达。正如前面所述,GC通过F-Queue队列和一专用线程完成finalize的执行。

对应的流程图如下所示:

可达状态空间

各状态含义如下:

  • finalized: 表示GC已经对该对象执行过finalize方法
  • reachable: 表示GC Roots引用可达
  • finalizer-reachable(f-reachable):表示不是reachable,但可通过某个finalizable对象可达
  • unreachable:对象不可通过上面两种途径可达

状态变迁图:

变迁说明:

1.新建对象首先处于[reachable, unfinalized]状态(A)

2.随着程序的运行,一些引用关系会消失,导致状态变迁,从reachable状态变迁到f-reachable(B, C, D) 或 unreachable(E, F)状态

3.JVM检测到处于unfinalized状态的对象变成f-reachable或unreachable。

JVM会将其标记为finalizable状态(G,H)。若对象原处于[unreachable, unfinalized]状态,则同时将其标记为f-reachable(H)。

在某个时刻,JVM取出某个finalizable对象,将其标记为finalized并在某个线程中执行其finalize方法。

4.由于是在活动线程中引用了该对象,该对象将变迁到(reachable, finalized)状态(K或J)。该动作将影响某些其他对象从f-reachable状态重新回到reachable状态(L, M, N)处于finalizable状态的对象不能同时是unreahable的。

5.将对象finalizable对象标记为finalized时会由某个线程执行该对象的finalize方法,致使其变成reachable。

注:System.runFinalizersOnExit()等方法可以使对象即使处于reachable状态,JVM仍对其执行finalize方法

代码示例

对象复活

public class GC {
	public static GC SAVE_HOOK = null;
    public static void main(String[] args) throws InterruptedException {
        SAVE_HOOK = new GC();
        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(500);
        if (null != SAVE_HOOK) { //此时对象应该处于(reachable, finalized)状态
            System.out.println("Yes , I am still alive");
        } else {
            System.out.println("No , I am dead");
        }
        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(500);
        if (null != SAVE_HOOK) {
            System.out.println("Yes , I am still alive");
        } else {
            System.out.println("No , I am dead");
        }
    }  

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("execute method finalize()");
        SAVE_HOOK = this;
    }
}

覆盖finalize方法以确保资源释放

作为一个补充操作,以防用户忘记“关闭“资源,JDK中FileInputStream、FileOutputStream、Connection类均用了此”技术“,下面代码摘自FileInputStream类

/**
 * Ensures that the <code>close</code> method of this file input stream is
 * called when there are no more references to it.
 *
 * @exception  IOException  if an I/O error occurs.
 * @see        java.io.FileInputStream#close()
 */
protected void finalize() throws IOException {
    if ((fd != null) &&  (fd != FileDescriptor.in)) {
        /* if fd is shared, the references in FileDescriptor
         * will ensure that finalizer is only called when
         * safe to do so. All references using the fd have
         * become unreachable. We can call close()
         */
        close();
    }
}

注意:我们自己手动调用finalize方法并不会影响到上述内部标记的变化,因此JVM只会至多调用finalize一次,即使该对象“复活”也是如此。我们手动调用多少次不影响JVM的行为

若JVM检测到finalized状态的对象变成unreachable,回收其内存(I),若对象并未覆盖finalize方法,JVM会进行优化,直接回收对象(O)

到此这篇关于深入了解Java中finalize方法的作用和底层原理的文章就介绍到这了,更多相关Java finalize方法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java9垃圾回收方法finalize() 原理解析

    1: finalize() 方法 finallize() 方法是Object类的方法, 用于在类被GC回收时 做一些处理操作, 但是JVM并不能保证finalize(0 ) 方法一定被执行, 由于finalize()方法的调用时机具有不确定性,从一个对象变得不可到达开始,到finalize()方法被执行,所花费的时间这段时间是任意长的.我们并不能依赖finalize()方法能及时的回收占用的资源,可能出现的情况是在我们耗尽资源之前,gc却仍未触发,因而通常的做法是提供显示的close()方法供客

  • Java垃圾回收机制的finalize方法实例分析

    本文实例讲述了Java垃圾回收机制的finalize方法.分享给大家供大家参考,具体如下: 一 点睛 finalize方法有如下四个特点: 永远不要主动调用某个对象的finalize方法,该方法应交给垃圾回收机制调用. finalize方法的何时被调用,是否被调用具有不确定性.不要把finalize方法当成一定会被执行的方法. 当JVM执行可恢复对象的finalize方法时,可能使该对象或系统中其他对象重新变成可达状态. 当JVM执行finalize方法时出现了异常,垃圾回收机制不会报告异常,程

  • Java之对象销毁和finalize方法的使用

    目录 对象的销毁 finalize方法 GC对对象的回收 在finalize方法中,是否重新给自己指定一个引用来避免被GC回收? 如果finalze方法中出现死循环会发生什么? 如果对象的创建出现这种死循环,会不会导致对象无法销毁进而导致内存溢出? 总结 对象的销毁 在C++中析构方法用于释放资源并且销毁对象本身. 在Java中,由于GC的存在,我们不需要手动回收内存,这大大减少了工作量,也提高了程序的安全性.但是Java也确实存在一个类似于C++中析构的函数. finalize方法 重载该方法

  • Java详解对象终止方法finalize()的用法

    finalize()方法机制 Java 语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑. 当GC去回收垃圾时, 总会在即将回收之前调用这个对象的 finalize()方法 , 一个对象finalize()方法只会被调用一次 finalize()方法可以被重写,通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件.套接字和数据库连接等. 我们一般最好不要主动去调用对象的finalize()方法, 理由有以下三点 : 1.在 finalize

  • Java中覆盖finalize()方法实例代码

    本文研究的主要是Java中关于覆盖finalize()方法的一次尝试,具体实现如下. 测试代码 package com.alioo.gc; /** * 执行结果: * */ public class FinalizeEscapeGC{ public static FinalizeEscapeGC instance=null; public void isAlive(){ System.out.println("yes,i am still alive"); } @Override pr

  • 深入了解Java中finalize方法的作用和底层原理

    目录 finalize方法是什么 finalize方法与C++的析构函数的区别 finalize方法合适清理的对象 可以触发finalize执行的方法 finalize实现对象再生问题 finalize的执行过程(生命周期) 对象对于finalize方法的两种状态 终结状态空间 可达状态空间 代码示例 对象复活 覆盖finalize方法以确保资源释放 finalize方法是什么 finalize方法是Object的protected方法,Object的子类们可以覆盖该方法以实现资源清理工作,GC

  • java多线程join()方法的作用和实现原理解析(应用场景)

    1.join() 方法的作用 这个方法的作用是先将当前线程挂起,待其他线程结束后在执行当前线程的代码: 2.应用场景 比如有三个人小红.小李.小王, 三个人相约一起去酒店吃饭,菜已经点好了, 三个人从不同的地方出发,只有三个人都到了酒店之后才会开始上菜:那么这三个人就分别代表三个线程,这三个线程执行完之后才会执行 "上菜" 的代码逻辑, 代码示例 package com.Lock; /** * join方法示例 * 比如有三个人小红.小李.小王, 三个人相约一起去酒店吃饭,菜已经点好了

  • 浅析JAVA中toString方法的作用

    因为它是Object里面已经有了的方法,而所有类都是继承Object,所以"所有对象都有这个方法". 它通常只是为了方便输出,比如System.out.println(xx),括号里面的"xx"如果不是String类型的话,就自动调用xx的toString()方法 总而言之,它只是sun公司开发java的时候为了方便所有类的字符串操作而特意加入的一个方法 回答补充:写这个方法的用途就是为了方便操作,所以在文件操作里面可用可不用例子1: 复制代码 代码如下: publ

  • 全面了解Java中Native关键字的作用

    初次遇见 native是在 java.lang.Object 源码中的一个hashCode方法: public native int hashCode(); 为什么有个native呢?这是我所要学习的地方.所以下面想要总结下native. 一.认识 native 即 JNI,Java Native Interface 凡是一种语言,都希望是纯.比如解决某一个方案都喜欢就单单这个语言来写即可.Java平台有个用户和本地C代码进行互操作的API,称为Java Native Interface (Ja

  • Java中finalize()详解及用法

     Java中finalize()详解 在程序设计中,我们有时可能希望某些数据是不能够改变的,这个时候final就有用武之地了.final是Java的关键字,它所表示的是"这部分是无法修改的".不想被改变的原因有两个:效率.设计.使用到final的有三种情况:数据.方法.类.        一. final数据 有时候数据的恒定不变是很有用的,它能够减轻系统运行时的负担.对于这些恒定不变的数据我可以叫做"常量"."常量"主要应用与以下两个地方: 1

  • Java中Class类的作用与深入理解

    Java中Class类的作用与深入理解 在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识.这个信息跟踪着每个对象所属的类.JVM利用运行时信息选择相应的方法执行.而保存这些信息的类称为Class.可能容易产生混淆,容易想到class.不过二者没什么关系,class不过是描述类的一个关键字.而Class却是保存着运行时信息的类. 它能做什么?Class类可以帮助我们在程序运行时分析类,说白了就是获取类中的值.可能瞬间就想到了反射,没错!Class一般就是和反射配套使

  • 浅谈Java中static关键字的作用

    static关键字主要有两种作用: 第一,为某特定数据类型或对象分配单一的存储空间,而与创建对象的个数无关. 第二,实现某个方法或属性与类而不是对象关联在一起 具体而言,在Java语言中,static主要有4中使用情况:成员变量.成员方法.代码块和内部类 (1)static成员变量: Java类提供了两种类型的变量:用static关键字修饰的静态变量和不用static关键字修饰的实例变量.静态变量属于类,在内存中只有一个复制,只要静态变量所在的类被加载,这个静态变量就会被分配空间,因此就可以被使

  • 详解Java中native方法的使用

    今天在网上学习时碰到有关于 native修饰符所修饰的方法,上网查了查,觉得很有意思记录一下 1.native简介 简单地讲,一个Native Method就是一个java调用非java代码的接口.一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C.这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数. native是与C++联合开发的时候用的!使用nat

  • 详解java中static关键词的作用

    在java中,static是一个修饰符,用于修饰类的成员方法.类的成员变量,另外可以编写static代码块来优化程序性能:被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问. static关键词的作用 1.静态成员变量的语法特定 2.静态函数的语法特定 3.静态代码块的语法特定 定义静态成员变量 Person.java class Person{ static int a; } 按照以前可以这么调用 public class Test1{

  • Java中反射机制和作用详解

    前言 很多刚学Java反射的同学可能对反射技术一头雾水,为什么要学习反射,学习反射有什么作用,不用反射,通过new也能创建用户对象. 那么接下来大师就带你们了解一下反射是什么,为什么要学习反射? 下面我们首先通过一个实例来说明反射的好处: 方法1.不用反射技术,创建用户对象,调用sayHello方法 1.1 我们首先创建一个User类 package com.dashi; /** * Author:Java大师 * User对象,包含用户的id和姓名以及sayHello方法 */ public

随机推荐