为啥 close() 输入流很好?

Posted

技术标签:

【中文标题】为啥 close() 输入流很好?【英文标题】:Why is it good to close() an inputstream?为什么 close() 输入流很好? 【发布时间】:2014-12-19 21:55:24 【问题描述】:

为什么需要close()java.io.InputStream或其子类?

现在有了java.io.OutputStream,比如说FileOutputStream,在写入一个文件之后,如果我们不close()输出流,我们打算写入文件的数据保留在缓冲区中并且不会被写入到文件中。

因此有必要close()OutputStream。但是在不关闭InputStream之后,我从未有过任何痛苦的经历。

但是互联网上的所有文章和书籍仍然说关闭任何流总是好的,可能是InputStreamOutputStream

所以我的问题是,为什么需要close()InputStream?人们说你可能会面临内存泄漏你不close()它。那么这是一种什么样的内存泄漏呢?

【问题讨论】:

资源泄漏在这里可能更令人担忧。处理输入流需要操作系统使用它的资源,如果你使用它后不释放它,你最终会耗尽资源。 在某些 JVM 中,底层内核将保持 FIS 打开(据我所知),直到您:A)关闭流或 B)JVM 退出。其他流(尤其是在处理套接字时)让其他东西知道您何时完成。例如,当从 Socket 关闭 OutStream 时,互联网另一端的相应输入流也会关闭。 @mailmindlin 不,它没有。没有流自动关闭。你必须自己关闭它们。 您不必为所有子类关闭它。例如,java.io.ByteArrayInputStream 不使用任何文件描述符,因此关闭它是无操作的,请参阅docs.oracle.com/javase/8/docs/api/java/io/… 【参考方案1】:

一个 InputStream 占用了一个很小的内核资源,一个低级文件句柄。此外,文件将在一定程度上被锁定(从删除、重命名),只要您将其打开以供读取。假设您不关心锁定的文件。最终,如果您需要读取另一个文件,并使用新的 InputStream 打开它,内核会依次为您分配一个新的描述符(文件流)。这最终会加起来。如果它是一个长时间运行的程序,那么程序失败只是时间问题。

处理器的文件描述符表通常大小有限。最终文件句柄表将用完进程的空闲槽。即使是数千个,您仍然可以轻松地为长时间运行的应用程序耗尽它,此时您的程序无法再打开新文件或套接字。

进程文件描述符表很简单,如下所示:

IOHANDLE fds[2048];  // varies based on runtime, IO library, etc.

您从占用 3 个插槽开始(STDIN、STDOUT、STDERR)。此外,任何网络套接字和其他类型的 IPC 都将使用同一张表中的插槽。填写它,您就对您的程序执行了拒绝服务。

所有这些很高兴知道;如何最好地应用它?

如果您依赖本地对象超出范围,那么它取决于垃圾收集器,它可以在自己的甜蜜时间收获它(不确定性)。不要依赖 GC,显式关闭流。

对于 Java,您希望在实现 java.lang.AutoCloseable 的类型上使用 try-with-resources,“其中包括所有实现 java.io.Closeable 的对象”,根据文档:https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

在 C# 中,等效于实现 IDisposable 的对象上的“使用”块

【讨论】:

如果是局部变量且方法返回时是否关闭?当然假设我们只使用了spring的ClassPathResource。 @pinkpanther - 您需要使用 try-with-resources 来确保,否则,由垃圾收集器决定何时收割它们(不确定性)。 docs.oracle.com/javase/tutorial/essential/exceptions/… -- 在 C# 中同样适用,但您会使用 using 语句。【参考方案2】:

这是潜在的资源泄漏。当您以这种方式提出问题时,继承使您无法确切知道哪些资源可能会泄漏。例如,我可以编写自己的名为 VoidInputStream 的类,它不分配需要关闭的资源。但是如果你不关闭它,你仍然违反了继承的合同。

有关不同输入流的列表,请参阅 http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html。

测试泄露的资源是出了名的困难。仅次于测试并发问题。不要太确定你没有在不知不觉中造成一点破坏。

【讨论】:

【参考方案3】:

可能有任意数量的 OS 资源与 InputStream 相关联,例如打开的文件或套接字。 close() 将释放这些资源。

您的程序不需要知道它正在使用哪种 InputStream。它只是应该遵守使用后关闭流的约定,以便可以释放任何资源。

【讨论】:

【参考方案4】:

这不是 内存 泄漏,而是文件句柄泄漏。操作系统只会允许单个进程打开一定数量的文件,如果你不关闭输入流,它可能会禁止 JVM 再打开。

【讨论】:

我说的是一般的 InputStream。而不仅仅是一个 FileInputStream。 @Aditya 您的 OutputStream 原始示例使用 FileOutputStream 来说明为什么需要关闭它,那么为什么这是一个不太有效的示例? :) 不,因为那是为了让你理解我的问题。但是这个答案是专门针对 FileInputStream 的。虽然我的问题一般是针对 InputStream 的。 :) 答案也是如此。大多数流最终会消耗内核中的 FD。它是指文件、套接字还是其他东西,完全无关紧要。 @Aditya 它代表“文件描述符”,但这是内核中的一个非常通用的术语,也用于套接字、通信端口……

以上是关于为啥 close() 输入流很好?的主要内容,如果未能解决你的问题,请参考以下文章

java 的输入输出流 如果在程序中没有close掉 那么关闭程序之后 流会自动关闭吗?

十输入/输出

第十周课程总结

Java IO流中先关闭输出流还是先关闭输入流?为啥?

为啥 HttpServletRequest 输入流为空?

为啥android HttpURLConnection 缓存输入流结果?