前言
Java是没有析构函数的,所以在一个类的生命周期结束时,它约等于猝死,谁知道啥时候没有人挂念了,系统又要gc了,然后就消失了,什么善后也做不了。但是有许多类都需要显式关闭资源,包括一些本地方法库,甚至JDK自带的很多类都需要(比如OutputStream)。
关闭资源除了人尽皆知的finalize方法,还有更优雅的try-with-resources、虚引用+cleaner方式。本文主要介绍后两种方式,因此也可以说明虚引用的作用。
不过需要强调的是:《Effective Java》一书指出,应尽可能地使用try-with-resourses机制来清理,finalize和cleaner最好作为兜底机制,因为这两种方式不能保证及时清理,甚至由于很大一部分逻辑是由JVM中的GC来实现的,因此不一定保证清理逻辑的执行。
先说说finalize()方法
记得在孤尽老师的课上听Object的8种方法如何记忆的时候,就提到finalize解决了对象的“我要到哪里去”的问题——跑题了
finalize方法从jdk9开始已经标记为废弃了,原因是(下面一段搬砖自jdk11的finalize方法的说明)
The finalization mechanism is inherently problematic. Finalization can lead to performance issues, deadlocks, and hangs. Errors in finalizers can lead to resource leaks; there is no way to cancel finalization if it is no longer necessary; and no ordering is specified among calls to finalize methods of different objects. Furthermore, there are no guarantees regarding the timing of finalization. The finalize method might be called on a finalizable object only after an indefinite delay, if at all. Classes whose instances hold non-heap resources should provide a method to enable explicit release of those resources, and they should also implement AutoCloseable if appropriate. The ref.Cleaner and ref.PhantomReference provide more flexible and efficient ways to release resources when an object becomes unreachable.
简而言之,它有问题(比如无法保证一定执行、何时执行、按什么顺序执行等等),java引入了更优雅的方式进行资源释放。主要有以下几种方式:
- 实现AutoCloseable接口(try-with-resource)
- 使用ref.Cleaner和ref.PhantomReference机制
那么,我们就展开说这两种方式。
自动清理资源的两种方式
实现AutoCloseable接口(try-with-resource)
jdk1.7引入了了try-with-resource的用法,大致如下(太懒了直接搬运这篇博文里的代码了)
public class TryWithResource {
public static void main(String[] args) {
try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
int b;
while ((b = bin.read()) != -1) {
bout.write(b);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
简而言之,在语法使用层面上,就是try后面跟一个括号,括号里声明并赋值一个需要显式关闭的资源。相比于之前的try-finally来说,更加优雅。(python也有类似用法,就是with xxx:)
那所有的类都能支持try-with-resource吗?No!但是难度也不大,只要实现了AutoCloseable接口就可以。这里直接搬运该接口的定义和官方说明:
/**
* An object that may hold resources (such as file or socket handles)
* until it is closed. The {@link #close()} method of an {@code AutoCloseable}
* object is called automatically when exiting a {@code
* try}-with-resources block for which the object has been declared in
* the resource specification header. This construction ensures prompt
* release, avoiding resource exhaustion exceptions and errors that
* may otherwise occur.
*/
public interface AutoCloseable {
void close() throws Exception;
}
简而言之,实现了这个接口,就能用try-with-resources语法。可以看到这个接口内部只有一个方法,就是close()。所以我们也可以想像一下,语法的实现大致就是和try-finally语句块一样,最后调