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()
    代价很高,尤其是因为(如果它做了任何事情)很可能触发完全的垃圾收集。这将导致跟踪堆中每个可访问对象中的每个引用