输入流被 GC 处理

发布于 2024-09-27 12:25:31 字数 483 浏览 4 评论 0原文

我知道如果我做类似的事情,

copyFromInToOut(new FileInputStream(f1), new FileOutputStream(f2));
System.gc();

它将在那些 FileInputStream 上运行 GC,并关闭它们。但是,如果我这样做,

copyFromInToOut(new BufferedInputStream(new FileInputStream(f1)), new BufferedOutputStream(new FileOutputStream(f2));
System.gc();

是否存在 FileOutputStream 在 BufferedOutputStream 之前被 GC 的危险,不会导致缓冲区刷新? 我不能调用flush、close,因为这需要比这更多的步骤。它首先涉及声明一个缓冲输入流,传递,然后调用 close。或者我这样做安全吗?

I know that if I do something like

copyFromInToOut(new FileInputStream(f1), new FileOutputStream(f2));
System.gc();

It will run the GC on those FileInputStreams, closing them. But if I do

copyFromInToOut(new BufferedInputStream(new FileInputStream(f1)), new BufferedOutputStream(new FileOutputStream(f2));
System.gc();

Is there any danger that the FileOutputStream will be GCed before the BufferedOutputStream, not causing the buffer to flush?
I can't call flush, close, because that takes more steps than this. It would first involve declaring a bufferedinputstream, passing, then calling close. OR am I safe to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

风吹短裙飘 2024-10-04 12:25:31

不要显式调用 System.gc()。不要依赖终结器来做任何事情。特别是如果您不了解垃圾收集的工作原理。可以忽略显式垃圾收集请求,并且终结器可能永远不会运行。

编写良好的流的 copyFromInToOut 方法可能会在内部使用其自己的缓冲区,因此不需要包装输出。

声明 FileInputStreamFileOutputStream 的变量,并在 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();
}

Don't call System.gc() explicitly. Don't rely on finalizers to do anything. Especially if you don't understand how garbage collection works. Explicit garbage collection requests can be ignored, and finalizers might never run.

A well-written copyFromInToOut method for streams is likely to use its own buffer internally, so wrapping the output should be unnecessary.

Declare variables for the FileInputStream and FileOutputStream, and invoke close() on each in a finally block:

FileInputStream is = new FileInputStream(f1);
try {
  FileOutputStream os = new FileOutputStream(f2);
  try {
    copyFromInToOut(is, os);
    os.flush();
  } finally {
    os.close();
  }
} finally {
  is.close();
}
司马昭之心 2024-10-04 12:25:31

据我所知,当GCd 时,没有任何InputStream 实现会close() 为您服务。你必须close()手动输入流。

编辑:显然FileInputStream在我不知道的finalize方法中为你做了close(),但是请参阅其他答案,了解为什么您不应该依赖于此。

在上面的两个示例中,您必须关闭输入和输出流。对于包装的缓冲区情况以及任何包装的情况,您应该只需要在最外层的 InputStream 上调用 close(),在本例中是 BufferedInputStream代码>

No InputStream implementation that I am aware with will close() for you when it is GCd. You MUST close() the InputStream manually.

EDIT: Apparently FileInputStream does close() for you in a finalize method which I wasn't aware of, but see other answers for the reason why you shouldn't rely on this.

In both your examples above you must close both the Input and Output streams. For the wrapped buffer case, and for any wrapped case, you should need only call close() on the outer-most InputStream, in this case BufferedInputStream

清泪尽 2024-10-04 12:25:31

分析当您这样做时到底发生了什么很有趣,只是不要这样做。

始终明确关闭您的流。

(回答你的问题,是的,当GC发生并且它们丢失时,缓冲区中的字节可能没有被刷新到文件I/O流)

well it's interesting to analyze what's really happening when you do that, just don't do that.

always close your streams explicitly.

(to answer your question, yes, bytes in buffers may not have been flushed to file i/o streams when gc occurs and they are lost)

深海夜未眠 2024-10-04 12:25:31

应使用 @erickson 的答案中显示的模式显式关闭流。依靠终结来关闭流是一个非常糟糕的主意

  1. 调用 System.gc() 的成本很高,特别是因为(如果它做了什么)可能会触发完整的垃圾收集。这将导致堆中每个可访问对象中的每个引用都被跟踪。

  2. 如果您阅读了 System.gc() 的 javadocs,您会发现它只是 JVM 运行 GC 的“提示”。 JVM 可以随意忽略提示...这将我们带到下一个问题。

  3. 如果您不显式运行 GC,则 GC 运行可能需要很长时间。即使这样,也不能保证终结器会立即运行。

同时:

  • 所有打开的文件保持打开状态,可能会阻止其他应用程序使用它们,
  • 流中任何未写入的数据都保持未写入状态,
  • 您的 java 应用程序甚至可能会遇到打开其他流导致文件描述符槽耗尽的问题。

依赖终结来处理输出流还有最后一个问题。如果流最终确定时存在未刷新的数据,则输出流类将尝试刷新它。但这一切都发生在 JVM 内部线程上,而不是应用程序的线程之一上。因此,如果刷新失败(例如,因为文件系统已满),您的应用程序将无法捕获所产生的异常,因此无法报告它......或执行任何恢复操作。

编辑

回到原来的问题,事实证明 BufferedOutputStream 类不会重写默认的 Object.finalize() 方法。因此,这意味着 BufferedOutputStrean 在被垃圾回收时根本不会被刷新。缓冲区中任何未写入的数据都将丢失。

这是明确关闭流的另一个原因。事实上,在这种特殊情况下,调用 System.gc() 不仅是不好的做法,而且是错误的做法。它还可能导致数据丢失。

Streams should be closed explicitly using the pattern shown in @erickson's answer. Relying on finalization to close streams for you is a really bad idea:

  1. Calling System.gc() is expensive, especially since (if it does anything) to is likely to trigger a full garbage collection. That will cause every reference in every reachable object in your heap to be traced.

  2. If you read the javadocs for System.gc() you will see that it is only a "hint" to the JVM to run the GC. A JVM is free to ignore the hint ... which takes us to the next problem.

  3. If you don't run the GC explicitly, it might be a long time until the GC runs. And even then, there's no guarantee that finalizers are run immediately.

In the mean time:

  • all open files stay open, possibly preventing other applications using them
  • any unwritten data in the streams remains unwritten
  • your java application might even run into problems opening other streams do to running out of file descriptor slots.

And there's one last problem with relying on finalization to deal with output streams. If there is unflushed data in the stream when it is finalized, an output stream class will attempt to flush it. But this is all happening on a JVM internal thread, not one of your application's threads. So if the flush fails (e.g. because the file system is full), your application won't be able to catch the resulting exception, and therefore won't be able to report it ... or do anything to recover.

EDIT

Returning to the original question, it turns out that the BufferedOutputStream class does not override the default Object.finalize() method. So that means that a BufferedOutputStrean is not flushed at all when it is garbage collected. Any unwritten data in the buffer will be lost.

That's yet another reason for closing your streams explicitly. Indeed, in this particular case, calling System.gc() is not just bad practice; it is also likely to lead to loss of data.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文