【问题标题】:Do I need to close InputStreamReader even if InputStream should remain open?即使 InputStream 应该保持打开状态,我是否需要关闭 InputStreamReader?
【发布时间】:2023-03-07 22:10:01
【问题描述】:

InputStream 作为参数从某处传递,在那里它将被进一步处理然后关闭。所以我不想在这里关闭InputStream。考虑以下代码:

void readInputStream(final InputStream inputStream) {
    final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    String line;
    while ((line = bufferedReader.readLine() != null) {
        // do my thing
    }
}

根据 Stackoverflow 的另一篇文章,如果我关闭 BufferedReader 和/或 InputStreamReader,那么底层的 InputStream 也将关闭。

我的问题:读者是否需要关闭,即使底层 InputStream 已在其他地方关闭?不关闭 Readers 会导致内存泄漏吗?

【问题讨论】:

  • 我不完全理解您的问题,但一般来说,一旦您使用完这些资源,就应该关闭它们。另外我建议看一下try-with-resources,它会自动为你关闭它们。最后你的代码 sn-p 不是最适合这个问题的。
  • 你的方法不应该创建自己的BufferedReader,而是接受它作为参数。这样,您的应用程序中就会有 一个 BufferedReader 并且可以在不再需要它时关闭它,这也将关闭底层InputStream
  • @Pshemo InputStream 被传递给不同的处理程序,这些处理程序用它做不同的事情。每次它在之前重置(并在最后关闭)。只有这个处理程序实际上需要一个 BufferedReader,另一个处理程序只是将 InputStream 传递给一个库函数等等。

标签: java memory memory-management bufferedreader inputstreamreader


【解决方案1】:

是否需要关闭阅读器,即使底层 InputStream 已在其他地方关闭?

不,他们绝对不需要在那种情况下。但无论如何,关闭它们通常是个好主意。

不关闭阅读器会导致内存泄漏吗?

不,没有内存泄漏,假设您完成后Reader 本身变得无法访问。此外,Reader 通常不会占用大量内存。

更重要的问题是不关闭Reader 是否会导致资源泄漏。答案是……视情况而定。

  • 如果您可以保证底层InputStream 将始终在应用程序的其他位置关闭,那么 会处理可能的内存泄漏。

  • 如果您不能保证,则存在资源泄漏的风险。底层操作系统级别的文件描述符是(例如)Linux 中的有限资源。如果 JVM 不关闭它们,它们可能会耗尽,并且某些系统调用将开始意外失败。

但是如果你关闭Reader,那么底层的InputStream关闭。

InputStream 上多次调用close() 是无害的,而且几乎没有成本。

不应该关闭Reader 的唯一情况是关闭底层InputStream 是错误的。例如,如果您关闭SocketInputStream,则应用程序的其余部分可能无法重新建立网络连接。同样,与System.in 关联的InputStream 通常无法重新打开。

在这种情况下,允许您在方法中创建的Reader 被垃圾收集实际上是安全的。与InputStream 不同,典型的Reader 类不会覆盖Object::finalize() 以关闭其数据源。


@Pshemo 提出了一个关于系统设计的重要观点。

如果您接受InputStream 作为参数,那么用本地Reader 包装它可能是错误的……尤其是BufferedReaderBufferedReader 可能会在流上预读。如果在您的方法返回后调用者将使用流,那么任何已读入缓冲区但未被此方法消耗的数据都可能丢失。

更好的办法是让调用者传递Reader。或者,应将此方法记录为取得InputStream 的所有权。在这种情况下,它应该总是close()它。

【讨论】:

  • 我的用例是用户可以上传一个文件,这个文件可以被许多不同的处理程序解释。所以第一个处理程序尝试以特定格式读取文件,如果不是那种格式,下一个处理程序将读取文件,依此类推。每次新处理程序读取该输入流时都会重置该输入流。这就是它被传递的原因。但是你的回答证实了我的想法,谢谢:)
【解决方案2】:

是的,读者需要关闭。使用代理,例如CloseShieldInputStream,防止传入的参数被关闭。

void readInputStream(InputStream inputStream) throws IOException{

  try (var bufferedReader = new BufferedReader(new InputStreamReader(
       new CloseShieldInputStream(inputStream)))) {

    String line;
    while ((line = bufferedReader.readLine()) != null) {
      // do my thing
    }
  }
}

JIC:与输入屏蔽类似,Apache Commons I/O 也提供了一个输出屏蔽来解决关闭包装输出流的类似问题,-CloseShieldOutputStream


有关更详细的注意事项,请参阅原始答案。感谢@stephen-c

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-25
    • 2011-03-28
    • 1970-01-01
    • 1970-01-01
    • 2011-06-19
    • 2016-09-09
    • 2013-01-07
    相关资源
    最近更新 更多