注:Java9之前只有终结方法,Java9出现了新的清除方法取代了终结方法。
终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的。清除方法没有终结方法那么危险,但仍然是不可预测、运行缓慢,一般情况下也是不必要的。
talk is cheap,show me the code。
定义一个Room类,实现AutoCloseable接口(使用try-with-resource语句块必须实现这个接口以保证资源被关闭),并实现其中的close()方法,其中还有一个静态内部类State,继承了Runnable接口
import java.lang.ref.Cleaner;
// An autocloseable class using a cleaner as a safety net (Page 32)
public class Room implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();
// Resource that requires cleaning. Must not refer to Room!
private static class State implements Runnable {
int numJunkPiles; // Number of junk piles in this room
State(int numJunkPiles) {
this.numJunkPiles = numJunkPiles;
}
// Invoked by close method or cleaner
@Override
public void run() {
System.out.println("Cleaning room");
numJunkPiles = 0;
}
}
// The state of this room, shared with our cleanable
private final State state;
// Our cleanable. Cleans the room when it’s eligible for gc
private final Cleaner.Cleanable cleanable;
public Room(int numJunkPiles) {
state = new State(numJunkPiles);
cleanable = cleaner.register(this, state);
}
@Override
public void close() {
cleanable.clean();
}
}
你也许会问,什么时候会调用State中的run()方法呢?以下会这种情况可能会触发:
1、通常是通过调用Room的close方法触发的,后者又调用了Cleanable的清楚方法。如果到了Room实例应该被垃圾回收时,客户端还没有调用close方法,清除方法就会(希望如此)调用State的run方法。
写两个测试程序就明白了,先看看第一个测试程序:
// Well-behaved client of resource with cleaner safety-net (Page 33)
public class Adult {
public static void main(String[] args) {
try (Room myRoom = new Room(7)) {
System.out.println("Goodbye");
}
}
}
代码中用到了try-with-resource语句块,比try-catch-finally靠谱(至少不会出现异常被覆盖的情况),保证myRoom一定会被回收,打印结果很明显,先是Goodbye,接着输出Cleaning room。作为对比,再看看下面这个糟糕的程序又如何:
// Ill-behaved client of resource with cleaner safety-net (Page 33)
public class Teenager {
public static void main(String[] args) {
new Room(99);
System.out.println("Peace out");
// Uncomment next line and retest behavior, but note that you MUST NOT depend on this behavior!
// System.gc();
}
}
你希望程序运行完了Room对象就回收了,然后输出Cleaning room,但事实是残酷的,仅仅输出了一行Peace out程序就结束了,在我的电脑环境下,只要在Teenager的main方法最后添加System.gc(),也就是把注释去掉,就可以让程序在退出的时候打印出Cleaning room,但是不能保证在你的机器上也能看到相同的行为,因为你根本不知道内存啥时候会被回收!
总而言之,除非是作为安全网,或者是为了终止非关键的本地资源,否则请不要使用清除方法,对于Java9之前的发行版本,则尽量不要使用终结方法。若使用了中介方法或者清除方法,则要注意它的不确定性和性能后果。