即使 InputStream 应该保持打开状态,我是否需要关闭 InputStreamReader?

Do I need to close InputStreamReader even if InputStream should remain open?

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
    }
}

如果我关闭 BufferedReader and/or InputStreamReader,那么基础 InputStream 也将被关闭,根据另一个 Whosebug post。

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

Do readers need to be closed, even if the underlying InputStream is closed somewhere else?

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

Can I get a memory leak by not closing readers?

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

更重要的问题是您是否可以通过不关闭 Reader 来获得 资源 泄漏。答案是……视情况而定。

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

  • 如果不能保证,那么就有资源泄露的风险。底层 OS 级别的文件描述符是(例如)Linux 中的有限资源。如果 JVM 不关闭它们,它们可以 运行 退出并且某些系统调用将开始意外失败。

但是如果您平仓Reader那么基础InputStream平仓。

InputStream 上多次调用 close() 是无害的,几乎不需要任何费用。

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

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


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

如果您接受 InputStream 作为参数,那么用本地 Reader 包裹它可能是错误的……尤其是 BufferedReaderBufferedReader 易于在流中预读。如果调用者 你的方法 returns 之后要使用流,那么任何已读入缓冲区但未被此方法使用的数据都可能是迷路了。

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

是的,读者需要关闭。使用代理,例如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