如果您幸运的话,其中一些类会实现AutoClosable,但有时您必须小心并检查现有方法,注意有close
,destroy
或{{ 1}}方法(或作者决定命名的方法)。
这是Java中资源泄漏的主要来源。
我和一位同事正在讨论这件事,并且也想知道:为什么这不能以某种方式实现自动化?
理论上,您可以使用shutdown
来处理此类情况,但它是not recommended。那么为什么没有办法只使用其中一些可关闭的资源并让GC在实例不再可用时自动关闭它们而不必记住明确写出一些finalize
处理代码(如try ...)?
这是因为在GC启动之前系统可能资源不足(文件描述符,...)?
注意:我尽可能使用autoclose并使用FindBugs(+ FB contrib)检查我的代码是否有内存泄漏,但我仍然想知道......
也感兴趣(如答案中所述):deprecation of finalize。
答案 0 :(得分:3)
垃圾收集器的唯一工作是收集不再使用的内存。添加资源关闭将对垃圾收集器的性能产生负面影响,并且当前由垃圾收集器调用的Finalizer
线程完成,以便允许实现在收集之前清除资源。值得注意的是,这个机制被声明为deprecated
,因为它从一开始就不是这种事情的最佳解决方案,但暂时可以实现你的类来清理自己,然后再去收集。
可能会扩展Finalizer
(或Java 9中的新mechanim)以检查要收集的类是否实现AutoClosable
(添加了Java 1.7的接口,所以它不是那么老了)并在finalize
之外调用它。这将产生与您提出的类似的效果,而无需更改垃圾收集器的行为和角色。也许这已经发生了(尚未自己测试过)。
答案 1 :(得分:2)
为什么这不能以某种方式自动化?
因为,一般来说,一个类可以做任何事情:编译器没有简单的方法可以知道某些东西已被打开"因此应该是"关闭",因为Java没有强烈的价值所有权概念。
即使你有一个需要关闭的类型的领域,你也不能轻易保证它不会从一个吸气剂返回,比如说是一个负责关闭它的东西。
指示类的实例需要关闭的唯一真正一致的方法是通过实现AutoCloseable
接口(或Closeable
或其他扩展它的接口)。如果您正在使用这些,IDE可能能够提供有关泄露资源的警告;但这些将是尽最大努力的。
答案 2 :(得分:2)
如果您愿意,可以创建自己的自动关闭资源,但是您需要做一些工作。
您可以编写一个管理器/工厂类,它对表示每个可关闭对象的对象保持弱引用。你分发了这个"代表"对客户端的客户端类和客户端使用此代表类来访问资源(它将包含对可关闭的引用并充当委托)。
这意味着工厂启动一个线程并保留一张地图:
<WeakReference<Representative>, Closable>
它可以迭代。如果代表已被垃圾收集(WeakReference将返回null),关闭可关闭并将其从地图中删除 - 集合中剩余的任何东西也可以在VM关闭时用钩子关闭 - 这可能是最危险的部分因为线程在关机期间仍然可以与Closable进行交互,但是我们拥有管理问题的所有工具。
您的可关闭资源本身从未由其代表和经理/工厂以外的任何人持有,因此其行为是可预测的。
我从来没有见过这样做 - 可能是因为它似乎比制作一个物品更具吸引力而且#34; Closable&#34; (并且更容易实现错误),但我不知道为什么它不起作用。
Java很难将其作为模式/语言功能实现,但通过向我们提供WeakReference,它为我们提供了使其成为可能的工具。
答案 3 :(得分:2)
对我来说,看起来所有答案都缺少重点:虽然GC可以处理资源关闭,但它不能足够快。
一个例子是内存映射文件的相当未解决的问题。当他们不再引用它们时,它们的映射会被清除,但与此同时,你可能会用完文件描述符或虚拟内存(这确实可能发生,因为它只限于几TB)。 / p>
这就是恕我直言this answer错误的原因。您可以通过这种方式处理资源(或使用finalize
或其他方式),但这还不够好。
答案 4 :(得分:1)
如果您正在开发公共模块(或公共util类),则可以使用execute around method pattern
来处理需要关闭的资源。所以这样你模块的用户就无法处理这些资源的关闭。(可能会阻止很多错误,因为人们可能会忘记关闭资源。)
Venkat先生有一个很好的演讲,他谈到了这个问题。观看接下来的10分钟,他精美地解释了这一点。https://youtu.be/e4MT_OguDKg?t=49m48s
以下是演示文稿中的示例代码;
public class Resource {
/*
* This represents a resource that needs to be closed.
* Since opening and closing the resource are done through use() method,
* Users of this resource don't have to care about resource is being closed or not.
* They just have to pass operations that they want to execute on the resource.
*/
private Resource() {System.out.println("created..");}
public Resource op1() {System.out.println("op1");return this;}
public Resource op2() {System.out.println("op2");return this;}
private void close() {System.out.println("closed..");}
public static void use(Consumer<Resource> consumer) {
Resource resource = new Resource();
try {
consumer.accept(resource);
}
finally {
resource.close();
}
}
}
public class SampleResourceUser {
/*
* This represents the user of the Resource,
* User only cares about which operations that needs to be done on the resource.
* Opening and closing the resource wrapped around the operation methods by the owner of the Resource.
*
*/
public static void main(String[] args) {
Resource.use(resource->resource.op1().op2());
}
}