finalize()

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()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 原文阅读 前言 这段时间懈怠了,罪过! 最近看到有同事也开始用上了微信公众号写博客了,挺好的~给他们点赞,这博客我...
    码农戏码阅读 6,018评论 2 31
  • 注:本文的目的并不是鼓励使用finalize方法,而是大致理清其作用、问题以及GC执行finalize的过程。 1...
    小陈阿飞阅读 1,469评论 0 3
  • [TOC] 内存管理 一、托管堆基础 在面向对象中,每个类型代表一种可使用的资源,要使用该资源,必须为代表资源的类...
    _秦同学_阅读 3,871评论 0 3
  • 预备知识 Java的Gc只负责内存的清理,其它方面的清理要程序员手工操作 调用Gc并不能保证Gc一定会执行,因为G...
    漠简尘阅读 1,700评论 0 0
  • Object类位于java.lang包中,java.lang包有最基础的和核心的类,在编译时会自动导入; Obje...
    遇见你的故事阅读 636评论 0 0