输入流被 GC 处理
我知道如果我做类似的事情,
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 FileInputStream
s, 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不要显式调用
System.gc()
。不要依赖终结器来做任何事情。特别是如果您不了解垃圾收集的工作原理。可以忽略显式垃圾收集请求,并且终结器可能永远不会运行。编写良好的流的 copyFromInToOut 方法可能会在内部使用其自己的缓冲区,因此不需要包装输出。
声明
FileInputStream
和FileOutputStream
的变量,并在finally
块中对每个变量调用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
andFileOutputStream
, and invokeclose()
on each in afinally
block:据我所知,当GCd 时,没有任何InputStream 实现会close()
为您服务。你必须close()
手动输入流。编辑:显然FileInputStream在我不知道的finalize方法中为你做了close(),但是请参阅其他答案,了解为什么您不应该依赖于此。
在上面的两个示例中,您必须关闭输入和输出流。对于包装的缓冲区情况以及任何包装的情况,您应该只需要在最外层的
InputStream
上调用close()
,在本例中是BufferedInputStream
代码>No InputStream implementation that I am aware with willclose()
for you when it is GCd. You MUSTclose()
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-mostInputStream
, in this caseBufferedInputStream
分析当您这样做时到底发生了什么很有趣,只是不要这样做。
始终明确关闭您的流。
(回答你的问题,是的,当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)
应使用 @erickson 的答案中显示的模式显式关闭流。依靠终结来关闭流是一个非常糟糕的主意:
调用
System.gc()
的成本很高,特别是因为(如果它做了什么)可能会触发完整的垃圾收集。这将导致堆中每个可访问对象中的每个引用都被跟踪。如果您阅读了
System.gc()
的 javadocs,您会发现它只是 JVM 运行 GC 的“提示”。 JVM 可以随意忽略提示...这将我们带到下一个问题。如果您不显式运行 GC,则 GC 运行可能需要很长时间。即使这样,也不能保证终结器会立即运行。
同时:
依赖终结来处理输出流还有最后一个问题。如果流最终确定时存在未刷新的数据,则输出流类将尝试刷新它。但这一切都发生在 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:
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.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.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:
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.