我知道如果我这样做
copyFromInToOut(new FileInputStream(f1), new FileOutputStream(f2));
System.gc();
它将在那些FileInputStream
上运行GC,关闭它们。但是如果我做的话
copyFromInToOut(new BufferedInputStream(new FileInputStream(f1)), new BufferedOutputStream(new FileOutputStream(f2));
System.gc();
在BufferedOutputStream之前是否存在FileOutputStream被GC的危险,而不是导致缓冲区刷新? 我不能调用flush,close,因为这需要更多的步骤。它首先涉及声明一个缓冲的输入流,传递,然后调用close。或者我可以安全地做到这一点吗?
答案 0 :(得分:8)
请勿明确调用System.gc()
。不要依赖终结者做任何事情。特别是如果您不了解垃圾收集的工作原理。可以忽略显式垃圾收集请求,并且终结器可能永远不会运行。
一个编写良好的流copyFromInToOut
方法可能会在内部使用自己的缓冲区,因此不必包装输出。
为FileInputStream
和FileOutputStream
声明变量,并在close()
块中的每个上调用finally
:
FileInputStream is = new FileInputStream(f1);
try {
FileOutputStream os = new FileOutputStream(f2);
try {
copyFromInToOut(is, os);
os.flush();
} finally {
os.close();
}
} finally {
is.close();
}
答案 1 :(得分:5)
在GCd时,我所知道的任何InputStream实现都不会 close()
。你必须手动close()
InputStream。
编辑:显然,FileInputStream会在我不知道的finalize方法中为你做close(),但是看看其他答案是因为你不应该依赖它。
在上面的两个示例中,您必须同时关闭输入和输出流。对于包装缓冲区案例以及任何包装案例,您只需要在最外层close()
上调用InputStream
,在这种情况下BufferedInputStream
答案 2 :(得分:2)
分析当你这样做时真正发生的事情很有意思,就是不这样做。
始终明确关闭您的信息流。
(回答你的问题,是的,当gc出现并丢失时,缓冲区中的字节可能没有被刷新到文件i / o流中)
答案 3 :(得分:2)
应使用@ erickson的答案中显示的模式显式关闭Streams。依靠最终化来为你关闭流是非常糟糕的主意:
调用System.gc()
是很昂贵的,特别是因为(如果它做了什么)可能会触发完整的垃圾回收。这将导致跟踪堆中每个可到达对象中的每个引用。
如果您阅读System.gc()
的javadoc,您将看到它只是JVM的“提示”以运行GC。 JVM可以自由地忽略提示......这将我们带到下一个问题。
如果您没有明确地运行GC,可能需要很长时间才能运行GC。即便如此,也无法保证终结器立即运行。
同时:
最后一个问题就是依靠finalization来处理输出流。如果在流完成时流中有未刷新的数据,则输出流类将尝试刷新它。但这一切都发生在JVM内部线程上,而不是您应用程序的一个线程。因此,如果刷新失败(例如因为文件系统已满),您的应用程序将无法捕获生成的异常,因此将无法报告它......或执行任何恢复操作。
修改强>
回到原始问题,结果是BufferedOutputStream类没有覆盖默认的Object.finalize()
方法。这意味着BufferedOutputStrean在被垃圾回收时根本不会被刷新。缓冲区中的任何未写入数据都将丢失。
这是明确关闭流的另一个原因。实际上,在这种特殊情况下,调用System.gc()
不仅仅是不好的做法; 可能导致数据丢失。