这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战
前言
避免使用Finalizer(Java 9中弃用)和cleaner(Java 9中替代finalizer)。原因很简单,
Finalizer 具有不可预测性,通常比较危险,并且一般情况下也没有必要使用。Cleaner 比 finalizer 危险性低一些,但仍然是不可预测的,会使程序运行缓慢,通常情况下依然没必要使用。
在 Java 中,当一个对象变得不可达时,垃圾回收器会回收与该对象相关联的存储空间,不需要程序员做专门的工作。 那些必须手动关闭的,在 Java 中,一般用 try-with-resources 或者 try-finally 块将之回收。
缺点
不能保证及时执行
Finalizer 和 Cleaner 的缺点之一是不能保证它们被及时执行。从一个对象变得不可达开始到它的 Finalizer 或 Cleaner 被执行,所花费的时间无法保证。这意味着不可控!不应该在Finalizer 和 Cleaner 中执行任何对时序要求严格(time-critical)的任务。
不同平台实现差别很大
虽说垃圾回收算法应该及时地执行 finalizer 和 cleaner,但是不同的 JVM 实现中差别很大。测试的可能好好地,结果上了用户的服务器就是不行。
会任意地延迟该类实例的回收过程
www.cnblogs.com/zhaoxinshan… 上文提到了添加finalizer之后报了内存溢出的问题。 原因就是给一个类添加 finalizer 会任意地延迟该类实例的回收过程。当这个 finalizer 线程的优先级比该程序的其它线程的优先级低,这时对象被终结的速度就比不上它们进入终结状态的速度。
不保证执行
Java 语言规范不仅不能保证 finalizer 或 cleaner 的及时执行,它甚至都不能保证它们会被执行。当一个程序终止时,某些已经无法访问的对象的 finalizer 却根本没有执行,这也是完全有可能的情况。所以,永远不要依靠 finalizer 或 cleaner 来更新重要的持久状态。
抛出的未被捕获的异常会被忽略
Finalizer 存在另外一个问题,在终结过程中抛出的未被捕获的异常会被忽略,并且对象的终止过程也会终止。未捕获的异常不仅难以排查,而且会导致对象有可能不完整。如果其他线程试图使用这些不完整的对象,将会导致任意不可预测的行为。通常,未被捕获的异常会终止线程并打印出整个堆栈错误,finalizer 中却不会这么做,因为错误被忽略,它甚至不会打印出任何一条警告信息。Cleaner 没有这个问题,因为使用 Cleaner 的库可以控制它的线程。
带来严重的性能损失
使用 finalizers 或 cleaners 会导致严重的性能损失。使用 try-with-resources 来关闭它并让垃圾回收器回收它所用的时间比finalizer 的时间要小很多倍。
用途
作为兜底方案来成为最后一道防线
finalizers的第一种用途是当对象的所有者忘记调用前面段落中建议使用的 close 方法的时,它们可以作为兜底方案出现。虽然不能保证 finalizer 或 cleaner 会及时运行(或者根本不运行),但在客户端无法正常结束操作的情况下,它们可以作为之后一道防线。如果你正考虑编写这样一个finalizer,建议你考虑考虑是否值得。一些 Java 库中的类,比如 FileInputStream、FileOutputStream、ThreadPoolExecutor 和 java.sql.Connection 就把 finalizer 作为了兜底的,防止用户忘了关资源。
回收本地对等体
Cleaner 的第二种合法用途与对象的本地对等体有关(native peer)。本地对等体是一个本地对象(native object,非 Java 对象),普通对象通过本地方法(native method)委托给一个本地对象。因为本地对等体不是普通对象,垃圾回收器并不知道它的存在,所以当它的 Java 对等体被回收的时候它不会被回收。假设程序性能是可接受的而且本地对等体不含有关键资源,这样的情况下,finalizer 或 cleaner 或许能派上用场。如果性能不允许或者本地对等体持有必须被及时回收的资源,你应该为类添加前面段落中建议的 close 方法。
总结
总之,不要使用 Cleaner 或 Java 9 之前的 Finalizer ,除非作为安全网或用来终止非关键的本地资源。即使这样,也要当心不确定性和性能影响。
终极解决方案
可以让你的类实现 AutoCloseable 接口,并且要求该类的客户端在每个实例不再被需要时调用 close 方法,通常使用 try-with-resources 来确保即使出现异常时亦能正常终止。有个值得注意的细节,实例必须持续跟踪自己是否被关闭:close 方法必须在一个域中记录该对象不再是有效的,如果该对象中的其他方法在对象被关闭之后调用,它们就必须检查这个域,并抛出 IllegalStateException 异常。