美文网首页
finalize()

finalize()

作者: 隔壁公司的程序员 | 来源:发表于2018-12-19 17:16 被阅读0次

    GC的几个观点


    1. 对象可能不被垃圾回收

    Java的垃圾回收遵循一个特点:只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交换给操作系统。这个策略是恰当的,因为垃圾回收本身也有开销,要是不使用它,那就不用支付这部分开销了

    2. 垃圾回收并不等于“析构”

    垃圾回收并不等价于C++中的析构函数(C++销毁对象必须用到这个函数),两者不是对应关系,第一点中就指出了垃圾回收的发生是不确定的,而C++中的析构函数是由程序员控制(delete)或者离开器作用域时自动调用发生的,其是在确定的时间对对象进行销毁并且释放其占用的内存

    3. 垃圾回收只与内存有关

    垃圾回收只能回收内存,而且只能回收内存中由Java创建对象方式(堆)创建的对象所占用的内存,无法回收其它资源,比如文件操作的句柄,数据库的连接等。

    4. 调用垃圾回收期(GC)不一定保证垃圾回收器的运行

    5. System.gc() 只是建议 jvm 执行,但是到底执行与否由 jvm 决定

    finalize()


    1. 一旦垃圾回收器准备释放对象所占的内存空间,如果对象覆盖了Object的finalize()并且函数体内不为空,就会首先调用对象的finalize(),然后在下一次垃圾回收动作发生的时候真正回收对象所占的空间

    2, 根据垃圾回收器的第三点,java垃圾回收器只能回收创建在堆中的java对象,对于不是这种方式创建的对象(例如JNI本地对象),只能通过finalize()保证使用之后进行销毁、释放内存

    3. 充当保证使用之后释放资源的最后一道屏障, 比如使用数据库连接之后未断开,并且由于程序员的个人原因忘记了释放连接, 这时就只能依靠finalize()函数来释放资源

    4. 《thinking in java》中所讲到的“终结条件”验证, 通过finalize()方法来试图找出程序的漏洞

    5. 任何对象的finalize()至多只能执行一次(程序员在代码中主动调用的不记录在此)

    尽量避免使用finalize():

    finalize()不一定会被调用, 因为java的垃圾回收器的特性就决定了它不一定会被调用.

    就算finalize()函数被调用, 它被调用的时间充满了不确定性, 因为程序中其他线程的优先级远远高于执行finalize()函数线程的优先级。也许等到finalize()被调用,数据库的连接池或者文件句柄早就耗尽了.

    如果一种未被捕获的异常在使用finalize方法时被抛出,这个异常不会被捕获,finalize方法的终结过程也会终止,造成对象出于破坏的状态。被破坏的对象又很可能导致部分资源无法被回收, 造成浪费.

    finalize()和垃圾回收器的运行本身就要耗费资源, 也许会导致程序的暂时停止.

    finalize的生命流程


    1. 当对象不可达时(GC Roots), GC会判断对象是否覆盖了finalize(), 若未覆盖则直接将其回收. 若对象覆盖并且未执行过(手动调用的不计此列)finalize(), 将其放入F-Queue队列, 由一个低优先级线程执行该队列中对象的finalize()方法。执行finalize()完毕后, Gc会再次判断该对象是否可达, 若不可达, 则进行回收, 否则, 对象“复活”

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

        unfinalized GC尚未调用对象的finalize(), 也不准备调用对象的finalize()

        finalizable GC尚未调用对象的finalize(), 但是会在之后的某个时间调用对象的finalize()

        finalized GC已经调用过对象的finalize()方法

        reachable 对象被任一存活线程所引用(比如在栈中有变量引用该对象等)

        finalizer-reachable 对象可以被处于终结状态的对象引用或者存在引用,但并不会被任何存活的线程访问到)

        unreachable 对象不可通过上面两种途径可达, 也就是不可到达, 没有被任何对象引用

    3. 具体状态转换

       状态转换图

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

    2. 随着程序的执行, 一些引用关系会消失, 导致状态变迁, 从reachable状态变迁为 f-reachable 或 unreachable

    3. 若JVM检测到处于 unfinalized 状态的对象变为 f-reachable 或 unreachable, JVM会将其标记为 finalizable 状态. 若对象原处于 [unreachable, unfinalized] 状态, 则同时将其标记为 f-reachable

    4. 在某个时刻, JVM 取出某个 finalizable 对象, 将其标记为 finalized 并在某个线程中执行其 finalize(). 由于是在活动线程中引用了该对象, 该对象将变迁为 [reachable, finalized] 状态. 该动作将影响某些其它对象从 f-reachable 状态重新回到 reachable 状态, 这就是对象重生

    5. 处于 finalizable 状态的对象不能同时是 unreachable 的, 由第4点知, 将对象从 finalizable 标记为 finalized 时会由某个线程执行该对象的finalize(), 致使其变成 reachable.

    6. 程序员手动调用 finalize() 并不会影响到上述内容的变化, 因此JVM只会至多调用 finalize() 一次, 即使该对象 ”复活”也是如此

    7. 若 JVM 检测到 finalized 状态的对象变为 unreachable, 回收内存

    8. 若对象未覆盖 finalize(), JVM会进行优化, 直接回收对象

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

    相关文章

      网友评论

          本文标题:finalize()

          本文链接:https://www.haomeiwen.com/subject/phojkqtx.html