1. finalize 的作用
finalize()是 Object 的 protected 方法, 子类可以覆盖该方法以实现资源清理工作, GC 在回收对象之前调用该方法
finalize()与 C++ 中的析构函数不是对应的 C++ 中的析构函数调用的时机是确定的(对象离开作用域或 delete 掉), 但 Java 中的 finalize 的调用具有不确定性
不建议用 finalize 方法完成非内存资源的清理工作, 但建议用于: 清理本地对象 (通过 JNI 创建的对象); 作为确保某些非内存资源(如 Socket 文件等) 释放的一个补充: 在 finalize 方法中显式调用其他资源释放方法其原因可见下文[finalize 的问题]
2. finalize 的问题
一些与 finalize 相关的方法, 由于一些致命的缺陷, 已经被废弃了, 如 System.runFinalizersOnExit()方法 Runtime.runFinalizersOnExit()方法
System.gc()与 System.runFinalization()方法增加了 finalize 方法执行的机会, 但不可盲目依赖它们
Java 语言规范并不保证 finalize 方法会被及时地执行而且根本不会保证它们会被执行
finalize 方法可能会带来性能问题因为 JVM 通常在单独的低优先级线程中完成 finalize 的执行
对象再生问题: finalize 方法中, 可将待回收对象赋值给 GC Roots 可达的对象引用, 从而达到对象再生的目的
finalize 方法至多由 GC 执行一次(用户当然可以手动调用对象的 finalize 方法, 但并不影响 GC 对 finalize 的行为)
3. finalize 的执行过程(生命周期)
(1) 首先, 大致描述一下 finalize 流程: 当对象变成 (GC Roots) 不可达时, GC 会判断该对象是否覆盖了 finalize 方法, 若未覆盖, 则直接将其回收否则, 若对象未执行过 finalize 方法, 将其放入 F-Queue 队列, 由一低优先级线程执行该队列中对象的 finalize 方法执行 finalize 方法完毕后, GC 会再次判断该对象是否可达, 若不可达, 则进行回收, 否则, 对象复活
(2) 具体的 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: 对象不可通过上面两种途径可达
状态变迁图:
变迁说明:
新建对象首先处于 [reachable, unfinalized] 状态(A)
随着程序的运行, 一些引用关系会消失, 导致状态变迁, 从 reachable 状态变迁到 f-reachable(B, C, D)或 unreachable(E, F)状态
若 JVM 检测到处于 unfinalized 状态的对象变成 f-reachable 或 unreachable,JVM 会将其标记为 finalizable 状态 (G,H) 若对象原处于 [unreachable, unfinalized] 状态, 则同时将其标记为 f-reachable(H)
在某个时刻, JVM 取出某个 finalizable 对象, 将其标记为 finalized 并在某个线程中执行其 finalize 方法由于是在活动线程中引用了该对象, 该对象将变迁到 (reachable, finalized) 状态 (K 或 J) 该动作将影响某些其他对象从 f-reachable 状态重新回到 reachable 状态(L, M, N)
处于 finalizable 状态的对象不能同时是 unreahable 的, 由第 4 点可知, 将对象 finalizable 对象标记为 finalized 时会由某个线程执行该对象的 finalize 方法, 致使其变成 reachable 这也是图中只有八个状态点的原因
程序员手动调用 finalize 方法并不会影响到上述内部标记的变化, 因此 JVM 只会至多调用 finalize 一次, 即使该对象复活也是如此程序员手动调用多少次不影响 JVM 的行为
若 JVM 检测到 finalized 状态的对象变成 unreachable, 回收其内存(I)
若对象并未覆盖 finalize 方法, JVM 会进行优化, 直接回收对象(O)
注: System.runFinalizersOnExit()等方法可以使对象即使处于 reachable 状态, JVM 仍对其执行 finalize 方法
4. 一些代码示例
(1) 对象复活
- [java] view plaincopy
- public
- class
- public
- static
- null
- public
- static
- void
- throws
- new
- null );
- ifnull
- else
- null );
- if
- null
- else
- protected
- void
- throws
- super
- this }
(2)覆盖 finalize 方法以确保资源释放
作为一个补充操作, 以防用户忘记关闭资源, JDK 中 FileInputStreamFileOutputStreamConnection 类均用了此技术, 下面代码摘自 FileInputStream 类
- [java] view plaincopy
- /**
- * 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
- throws
- ifnull * will ensure that finalizer is only called when
- * safe to do so. All references using the fd have
- * become unreachable. We can call close()
- */ }
参考:
12.6 Finalization of Class Instances https://notendur.hi.is//~snorri/SDK-docs/lang/lang083.htm
深入理解 java 的 finalize http://zhang-xzhi-xjtu.iteye.com/blog/484934
The Finalizable Object http://www.artima.com/interfacedesign/Finalizable.htm
来源: http://www.bubuko.com/infodetail-2506939.html