【问题标题】:Do unclosed streams cause memory leaks in java?java - 未关闭的流会导致内存泄漏吗?
【发布时间】:2014-05-18 07:38:10
【问题描述】:

我相信开放流会导致 java 中的内存泄漏(至少 java 1.6 和更早版本确实有这个问题)。

但是,在搜索时(甚至在这里),我发现有些人同意这一点,而另一些人则不同意。所以,如果我写这个程序:

import java.io.*;
public class CreatingMemoryLeak {

    public static void main(String args[])
    {
        String s = "xxxxxxx";
        InputStream in = new ByteArrayInputStream(ss.getBytes());
        BufferedInputStream bf = new BufferedInputStream(in);

        try {
            while(bf.read()>0)
            {
                System.out.println("got it");
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Here is a input stream " + s +" causing a memory leak");

    }
}

如果我不明确关闭bf 流,会导致内存泄漏吗?

【问题讨论】:

  • 注意,通常close的想法是释放原生资源(比如C堆分配的数据需要deleted)。 ByteArrayInputStream specifically says close has no effect on it 的文档。所以我认为这不是你在这里实际要求的一个很好的例子。

标签: java memory-management memory-leaks


【解决方案1】:

对于 Java,准确使用术语“内存泄漏”非常重要。

在 Java 中,当您的代码永久持有一个引用时,就会发生内存泄漏,因此某些对象永远不会被垃圾回收。

从这个意义上说,未能关闭流并不是内存泄漏。具有本机资源的流具有终结器; GC 最终会关闭它们。除非您持有对未关闭流的引用,否则它不是泄漏。

但是,除了内存泄漏之外,还有其他类型的泄漏。大多数操作系统都会限制打开文件的数量。如果你没有关闭你的流,GC 可能需要很长时间才能为你关闭它们;最终结果可能是您用完了系统文件描述符,并且您的代码无法再打开一个文件。有人会称这是泄漏,但称其为内存泄漏并不准确。

【讨论】:

    【解决方案2】:

    (至少 java 1.6 和更早的版本确实有这个问题)。

    任何版本的 Java,以及任何语言,都有这个问题;它不是 Java 特有的。

    如果你得到一个需要系统资源的输入或输出句柄,无论如何你都需要释放它们。

    Java 有Closeable 表示您持有的实例可能或可能不持有系统资源;但是如果它这样做了,如果你不.close()它,你会泄漏资源;就这么简单。

    例如,您可能有一个以InputStream 作为参数的方法;好的,好的,但这是什么InputStream?是FileInputStream 还是ByteArrayInputStream,还是别的什么?你无法知道。第一个需要正确关闭,但第二个不需要。

    那又怎样? .close() 无论如何,你没有什么可失去的。你真的想抓住这个机会吗?

    在 Java 6 中,您需要使用 Guava 的 Closer,因为这是关闭所有资源的最安全方法(JDK 不提供这样的工具):

    final Closer closer = Closer.create();
    
    try {
        final InputStream in = closer.register(openInHere());
        final InputStream in2 = closer.register(...);
        final OutputStream out = closer.register(...);
        // do work with in, in2, out, other
    } catch (WhateverException e) {
        // IF WhateverException is not an IOException and you want to rethrow...
        // throw closer.rethrow(e, WhateverException.class);
    } finally {
        closer.close(); // always safe
    }
    

    使用 Java 7,您可以使用 try-with-resources 来处理所有 AutoCloseable 资源(Closeable 扩展):

    try (
        final InputStream in = ...;
        // etc
    ) {
        // work with AutoCloseable resources
    } catch (WhateverException e) {
        // deal with e, rethrow if necessary
    }
    

    Closer 和 try-with-resources 之间的主要区别在于,对于后者,资源将在 之前 catch 关闭,而 Closer 将在 finally 中关闭它们。

    但再说一遍:不要冒险。全部关闭。

    【讨论】:

      【解决方案3】:

      答案取决于流。

      内存泄漏只是一种情况,即您保留了您不打算做的事情。这总是由于编程错误:某个地方的某个人忘记释放(从所有强引用中“分离”)某些实例。这些随着时间的推移加起来,然后你会注意到“泄漏”。无限增长的缓存就是一个很好的例子。

      例如,一个流可以将一些数据放入一个共享容器(通常是静态的),如资源锁、某种缓存等,一旦它被打开/使用。然后它在 close 方法中清理这个资源。所以跳过这最后一部分确实会造成内存泄漏。

      在您的示例中,ByteArrayInputStream.close() 方法什么都不做,所以那里没有问题。 BufferedInputStream.close() 只是将调用委托给包装类,所以在这种情况下,再次没有问题。

      更复杂的流处理文件,网络流如果不关闭可能会造成泄漏,但这种情况并不常见。我假设流本身没有保留,而是可供收集。在许多情况下,“智能”流甚至可以在其自己的收集过程中修复这些疏忽并自行执行必要的清理(这仍然是流应该清楚地记录/通知某处的异常情况)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-23
        • 2021-09-25
        • 2014-12-08
        • 1970-01-01
        相关资源
        最近更新 更多