Java 正在GCed的输入流
我知道如果我这样做Java 正在GCed的输入流,java,garbage-collection,fileoutputstream,Java,Garbage Collection,Fileoutputstream,我知道如果我这样做 copyFromInToOut(new FileInputStream(f1), new FileOutputStream(f2)); System.gc(); 它将在那些FileInputStreams上运行GC,并关闭它们。但如果我这样做了 copyFromInToOut(new BufferedInputStream(new FileInputStream(f1)), new BufferedOutputStream(new FileOutputStream(f2))
copyFromInToOut(new FileInputStream(f1), new FileOutputStream(f2));
System.gc();
它将在那些FileInputStream
s上运行GC,并关闭它们。但如果我这样做了
copyFromInToOut(new BufferedInputStream(new FileInputStream(f1)), new BufferedOutputStream(new FileOutputStream(f2));
System.gc();
是否存在FileOutputStream在BufferedOutputStream之前被GCD的危险,而不会导致缓冲区刷新?
我不能称之为flush,close,因为这需要更多的步骤。它首先需要声明一个bufferedinputstream,传递,然后调用close。或者我这样做安全吗?我所知道的任何InputStream实现都不会在GCd时为您关闭()。您必须手动关闭输入流
编辑:显然,FileInputStream确实以finalize方法为您关闭了(),我不知道这一点,但请查看其他答案,了解您不应该依赖此方法的原因
在上述两个示例中,必须同时关闭输入流和输出流。对于已包装的缓冲区情况,以及任何已包装的情况,您只需要在最外层的InputStream上调用close()
,在这种情况下,BufferedInputStream
当它是GCd时,我所知道的没有InputStream实现将为您调用close()
。您必须手动关闭输入流
编辑:显然,FileInputStream确实以finalize方法为您关闭了(),我不知道这一点,但请查看其他答案,了解您不应该依赖此方法的原因
在上述两个示例中,必须同时关闭输入流和输出流。对于包装好的缓冲区,以及任何包装好的缓冲区,您只需要在最外层的
InputStream
上调用close()
,在这种情况下BufferedInputStream
很有意思的一点是,当您这样做时,分析真正发生的事情,只是不要这样做。
始终明确关闭流
(为了回答您的问题,是的,当gc发生时,缓冲区中的字节可能没有被刷新到文件i/o流中,并且它们丢失了)那么分析当您这样做时真正发生的事情是很有趣的,只是不要这样做。 始终明确关闭流 (为了回答您的问题,是的,当gc发生时,缓冲区中的字节可能没有被刷新到文件i/o流,并且它们丢失)不要显式调用
System.gc()。不要依赖终结器来做任何事情。特别是如果你不明白垃圾收集是如何工作的。可以忽略显式垃圾收集请求,终结器可能永远不会运行
流的编写良好的copyFromInTout
方法可能会在内部使用自己的缓冲区,因此不需要包装输出
为FileInputStream
和FileOutputStream
声明变量,并在finally
块中对每个变量调用close()
:
FileInputStream is = new FileInputStream(f1);
try {
FileOutputStream os = new FileOutputStream(f2);
try {
copyFromInToOut(is, os);
os.flush();
} finally {
os.close();
}
} finally {
is.close();
}
不要显式调用System.gc()
。不要依赖终结器来做任何事情。特别是如果你不明白垃圾收集是如何工作的。可以忽略显式垃圾收集请求,终结器可能永远不会运行
流的编写良好的copyFromInTout
方法可能会在内部使用自己的缓冲区,因此不需要包装输出
为FileInputStream
和FileOutputStream
声明变量,并在finally
块中对每个变量调用close()
:
FileInputStream is = new FileInputStream(f1);
try {
FileOutputStream os = new FileOutputStream(f2);
try {
copyFromInToOut(is, os);
os.flush();
} finally {
os.close();
}
} finally {
is.close();
}
应该使用@erickson的答案中所示的模式显式关闭流。依靠定稿为您关闭流是一个非常糟糕的主意:
调用System.gc()
代价很高,尤其是因为(如果它做了任何事情)很可能触发完全的垃圾收集。这将导致跟踪堆中每个可访问对象中的每个引用
如果您阅读System.gc()
的javadocs,您将看到它只是JVM运行gc的一个“提示”。JVM可以随意忽略提示。。。这就引出了下一个问题
如果不显式运行GC,则可能需要很长时间才能运行GC。即使这样,也不能保证终结器立即运行
同时:
- 所有打开的文件都保持打开状态,可能会阻止其他应用程序使用它们
- 流中任何未写入的数据都保持未写入状态
- 您的java应用程序甚至可能在打开其他流时遇到问题,因为文件描述符插槽不足
最后一个问题是依靠终结来处理输出流。如果最终确定流时流中有未刷新的数据,则输出流类将尝试刷新它。但这一切都发生在JVM内部线程上,而不是应用程序的一个线程上。因此,如果刷新失败(例如,因为文件系统已满),您的应用程序将无法捕获结果异常,因此将无法报告它。。。或者做任何事情来恢复
编辑
回到原来的问题,BufferedOutputStream类没有覆盖默认的Object.finalize()
方法。因此,这意味着在垃圾收集时,BufferedOutputStrean根本不会被刷新。缓冲区中任何未写入的数据都将丢失
这是明确关闭流的另一个原因。实际上,在这种特殊情况下,调用System.gc()
不仅仅是一种不好的做法;它还可能导致数据丢失。应使用@erickson的回答中所示的模式显式关闭流。依靠定稿为您关闭流是一个非常糟糕的主意:
调用System.gc()
代价很高,尤其是因为(如果它做了任何事情)很可能触发完全的垃圾收集。这将导致跟踪堆中每个可访问对象中的每个引用