2015-03-02 68 views
14

在阅读https://www.airpair.com/java/posts/spring-streams-memory-efficiency之后,我很想将结果从数据库中流出来,但正如我与一位同事讨论的一样(他在该文章中添加了cfr评论),需要记住使用try-with-resources构造来避免任何内存泄漏。为什么终端操作发出后Java不关闭()流?

  1. 为什么不是Java 8库照顾闭流本身每个terminal operation后(无需换流实例在try-与资源)?
  2. 如果适用,是否有任何计划将此功能添加到Java中,还是有意义请求它?
+0

'terminal operation'是什么意思? – 2015-03-02 15:40:39

+9

我认为你正在将'java.io.InputStream'与'java.util.stream.Stream'混合,这是两个非常不同的概念 – 2015-03-02 15:41:21

+3

@Zoltán不,我的确在谈论java.util.Stream,是'AutoCloseable',因此可以在'try-with-resources'内使用。 @MartinSerrano我添加了一个链接到Java的文档,涉及到一般的流和终端操作。 – 2015-03-02 15:47:10

回答

19

因为需要显式资源释放的流实际上是一个很不寻常的情况。所以我们选择不将所有流执行都用于仅对.01%用法有价值的东西。

我们制作了Stream Autocloseable,以便您可以从发布资源,如果您愿意,但这是我们停下来的原因。

这样做不仅会自动为大多数用户提供他们不需要的额外工作,而且这也会违反一般原则:谁分配资源负责关闭资源。当你调用

BufferedReader reader = ... 
reader.lines().op().op()... 

是一个开放的资源,而不是流库,并应该关闭它。事实上,由于关闭通过调用某个资源占用对象上的访问器方法而产生的流有时会关闭底层对象,因此您可能不希望流为您关闭BufferedReader--您可能希望它在呼叫。

如果你想关闭资源,这是太容易了:

try (BufferedReader reader = ...) { 
    reader.lines().op()... 
} 

你可能用一种特殊的方式流,所以它可能看起来“很明显”应该做什么流 - 但有比你更多的用例。因此,我们不应该满足特定的用例,而是从一般原则开始:如果您打开了流,并且希望它关闭,请自行关闭它,但如果您未打开它,则不适合关闭它。

+0

这可能是一个catch-22问题,需要资源释放的流可能很少见,因为当前的库无法支持他们的情况。 AFAIK,'Stream'主要是为了支持并行化而引入的,但是从它的API合约来看,并行化只是它应该有助于做的一件事。我确定许多开发人员可能希望将流API用于其他目的,例如O(1)内存使用,避免停止世界GC事件以及清晰分离迭代和处理问题(我只是重用马可托波托尼克的措辞在这里)。 – 2015-03-02 16:43:50

+1

我现在更好理解为什么'Stream'目前是这样的,所以我会接受这个答案。但对于我作为最终用户和我目前的理解,在Java库中仍然需要有一个(nother?)流实现来处理finally块中的底层资源。 – 2015-03-02 16:45:49

+2

更正:引入流以提供更加抽象的方式来表达对任意数据集的集合操作;这是实际并行性的必要先决条件,但我认为每个人都会同意Stream即使没有并行性也很有用。对'close()'和'Autocloseable'的支持表示扩展了设计,为源代表用户管理资源(而不是内存,由VM管理)的流提供最小支持。我认为这真的是“2%空”与“98%满”,你现在可能只是以2%的比例游泳。 – 2015-03-02 17:38:34

0

为什么不是Java 8库照顾收盘的每一个终端操作之后流本身(而不必换流实例在try-与资源)?

由于在终端操作期间或之前可能会发生异常,并且您可能不希望终端操作关闭流。如果您确实希望关闭流,则可以使用try-with-resource。

如果适用,是否有任何计划将此功能添加到Java,或者它是否有意义请求它?

这没有意义,请参阅上面的答案。

+0

你有参考吗? – 2015-03-02 15:52:32

+0

@MartinSerrano究竟是什么的参考?在执行终端操作之前,打开一个流并导致异常是微不足道的。 – davmac 2015-03-02 15:52:58

+0

为您的答案。这只是你的意见吗?似乎@Zoltan的答案是更多的目标。 – 2015-03-02 15:55:02

3

我想你在混合java.io.InputStreamjava.util.stream.Stream,这是两个非常不同的概念。

try-with-resources作用于执行Autoclosable接口的对象,如InputStream s。 InputStream代表与IO有关的数据的抽象来源。

java.util.stream.Stream<T>另一方面,从函数式编程实现了一个概念,它表示一种动态集合,它不一定是静态构建的,而是可以生成的,因此可能是无限的。

Marko Topolnik(您链接的文章的作者)在本文中基本上做了什么,提出了一种将IO源代码包装为java.util.stream.Stream的方法。这是一个非常聪明的方法,但java.util.stream.Stream一般不是用于此目的。

因为它们通常不是用于IO,所以在终端操作之后没有理由包括关闭


编辑

你澄清之后,你有没有在实际上混淆了两个(对不起,假设如此),这要归功于this answer,我发现你的确切实例回答在documentation of AutoCloseable(强调我自己加的):

这是可能的,而且实际上很常见的,一个基类来实现 AutoCloseable即使不是所有它的子类或实例将的持有可释放资源。对于必须完整运行的代码,或者知道AutoCloseable实例 需要资源释放时,建议使用试用资源 结构。 但是,如果使用诸如Stream之类的工具,则支持基于I/O和非基于I/O的表单,但在使用非基于I/O的表单时通常不需要使用块。

+0

'java.util.stream.Stream'也实现'Autoclosable',这正是这个问题的关键。 – davmac 2015-03-02 15:55:15

+0

@davmac感谢您指出,我编辑了我的答案。 – 2015-03-02 15:59:51