简书:capo 转载请注明原创出处,谢谢!
前言:
今天我们来看看Object中一个经常被人遗忘的方法,finalize方法。老规矩,我们先看看Javadoc是怎样描述这个方法的
/**
* Called by the garbage collector on an object when garbage collection
* determines that there are no more references to the object.
* A subclass overrides the {@code finalize} method to dispose of
* system resources or to perform other cleanup.
* <p>
* The general contract of {@code finalize} -is that it is invoked
* if and when the Java™ virtual
* machine has determined that there is no longer any
* means by which this object can be accessed by any thread that has
* not yet died, except as a result of an action taken by the
* finalization of some other object or class which is ready to be
* finalized. The {@code finalize} method may take any action, including
* making this object available again to other threads; the usual purpose
* of {@code finalize}, however, is to perform cleanup actions before
* the object is irrevocably discarded. For example, the finalize method
* for an object that represents an input/output connection might perform
* explicit I/O transactions to break the connection before the object is
* permanently discarded.
* <p>
* The {@code finalize} method of class {@code Object} performs no
* special action; it simply returns normally. Subclasses of
* {@code Object} may override this definition.
* <p>
* The Java programming language does not guarantee which thread will
* invoke the {@code finalize} method for any given object. It is
* guaranteed, however, that the thread that invokes finalize will not
* be holding any user-visible synchronization locks when finalize is
* invoked. If an uncaught exception is thrown by the finalize method,
* the exception is ignored and finalization of that object terminates.
* <p>
* After the {@code finalize} method has been invoked for an object, no
* further action is taken until the Java virtual machine has again
* determined that there is no longer any means by which this object can
* be accessed by any thread that has not yet died, including possible
* actions by other objects or classes which are ready to be finalized,
* at which point the object may be discarded.
* <p>
* The {@code finalize} method is never invoked more than once by a Java
* virtual machine for any given object.
* <p>
* Any exception thrown by the {@code finalize} method causes
* the finalization of this object to be halted, but is otherwise
* ignored.
*
* @throws Throwable the {@code Exception} raised by this method
* @see java.lang.ref.WeakReference
* @see java.lang.ref.PhantomReference
* @jls 12.6 Finalization of Class Instances
*/
protected void finalize() throws Throwable { }
梳理一下上面规范讲述的几点
- 访问垃圾收集在对象上当垃圾收集这确定这个对象上没有更多的引用时可能会触发finalize方法
- 一个子类重写了finalize方法处理系统资源或者执行其它的清理也就是调用System.gc可能会触发
- 这个一般的合约规定finalize,如果Java虚拟机已经确定这不再有任何意味这个对象能通过任何一个尚未死亡的线程访问的方法调用,除非是由于最后确定的其它对象或类的准备工作所采取的行动.
- finalize方法可以采取任何行动,包括使此对象再次可用于其它线程,finalize的通常是在对象不可撤销地丢弃之前执行清楚动作.例如:表示输入输出连接的对象的finalize方法可能会在对象被永久丢弃之前执行显式I/O事务来中断连接
Object中finalize操作只是返回一个正常线程 - Java规范确保调用finalize的线程在调用finalize方法时不会持有任何用户可见的同步锁
- 如果finalize方法抛出未捕获的异常,则会忽略该异常,并终止该对象的定类。
- finalize方法不会被任何对象调用多次
- finalize方法抛出的任何异常都会导致该对象的终止被停止,否则就忽略
针对上述规范,我有以下几点疑惑
finalize到底是使用在什么应用场景
通常我们知道对象创建完,要做清理操作,当然GC会清理哪些由new分配的内存,但是如果不是通过new分配的内存清理的话,就需要调用finalize方法.
它的工作原理:一旦垃圾回收器准备释放对象占用的内存空间,将首先调用finalize方法,并且在下一次垃圾回收动作发生,才会真正的回收该对象占用的内存,也就是finalize方法会在垃圾回收器真正回收对象之前调用
finalize到底是不是C++中的析构函数
在C++中销毁对象必须用到析构函数,且兑现一定会被销毁(如果程序没有缺陷),但是Java里的对象并非总是被回收.也就是:
- 对象有可能没有被垃圾回收
- 垃圾回收并不是等于析构(因为析构一定会销毁对象)
我们使用finalize这个方法有时候是为了确保某个对象已经被清楚,我们自己手动的去清除这个对象.
一般只要程序没有濒临储存空间用完的那一刻,对象占用的存储空间就总也得不到释放.如果这个程序结束了,垃圾回收器一直没有释放掉你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还给操作系统.个人觉得这个操作策略是正确的,能不用垃圾回收去处理就不出来,毕竟就省去了这部分开销。
所以finalize并不会确保对象会被销毁,它不是析构函数.
finalize另外一个用途(判断对象在清理时是否安全释放)
前面已经说过它可能是用来清理一些不是用过new创建的内存对象,那么这些对象就是指的是Java中的本地方法创建的对象
无论是垃圾回收还是终结方法(finalize)都不能不保证一定会发生,因为它们发生的条件是对象被回收(这个条件是比较苛刻的,JVM只有在濒临内存耗尽的情况下才会触发)
finalize可能会用于对象终结条件的验证:
我们可以用finaliz来检测对象被清理之前是不是还有什么操作没有执行,比如当某个对象打开了一个文件,在对象被回收之前程序应该关闭这个文件。使用finalize我们可以确保对象对安全的释放掉了.
下面看finalize一个用于检验对相关是否安全释放的例子:
package com;
/**
* Created by XMl on 2017/9/8.
*/
public class Book {
public boolean checkedOut = false;
public Book(boolean checkedOut) {
// checkedOut = checkedOut;
this.checkedOut = checkedOut;
}
public void checkIn() {
checkedOut = false;
}
/**
* 重写finalize方法
*
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
//校验checkedOut
if (checkedOut) {
System.out.println("校验出现了一次错误: Checked out ");
}
// super.finalize();
}
}
我们写一测试类
package com.rtti.xml;
import com.Book;
/**
* Created by XMl on 2017/9/8.
*/
public class TerminationCondition {
public static void main(String[] args) {
Book novel = new Book(true);
novel.checkIn();
//对checked进行了一次误操作
new Book(true);
//调用Gc 强制执行终结操作(finalize)
System.gc();
}
}
打印信息:
我们看到输出校验信息.但是我们调用System.gc的时候并没有回收对象内存
总结:
- 垃圾回收内存是JVM的一个不确定操作,通常会在系统濒临内存溢出可能会回收,所以你不要指望垃圾回收来控制finalize方法的调用
- finalize方法通常是用于清理一些非本地方法(native),和一些对象安全释放校验的操作