Do unclosed streams cause memory leaks in java?

2019-01-14 14:05发布

问题:

I believe open streams cause memory leak in java (at least java 1.6 and earlier did had this problem).

But, while searching (even here), I found some people agreeing with this while others do not. So, if I write this program:

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");

    }
}

and if I do not close the bf stream explicitly, will it cause a memory leak?

回答1:

It's important to be precise in the use of the term 'memory leak' with respect to Java.

In Java, a memory leak happens when your code holds a reference permanently, so that some object is never garbage collected.

Failing to close a stream is not a memory leak in this sense. Streams that have native resources have finalizers; the GC will eventually close them down. Unless you hold a reference to the unclosed stream it isn't a leak.

However, there are other kinds of leaks besides memory leaks. Most operating systems limit the number of open files. If you fail to close your streams, the GC may take a very long time to close them for you; the net result may be that you run out of system file descriptors and your code fails to open one more file. Some people will call this a leak, but it's not accuracy to call it a memory leak.



回答2:

(at least java 1.6 and earlier did had this problem).

ANY version of Java, and for that matter, ANY language, has this problem; it is not specific to Java.

If you get hold of an input or output handle which needs system resources, you need to release them no matter what.

Java has Closeable to signify that the instance you hold may, or may not, hold system resources; but in the event that it does, if you don't .close() it you'll leak resources; it's that simple.

For instance, you may have a method which has an InputStream as an argument; OK, fine, but what is this InputStream? Is it a FileInputStream or a ByteArrayInputStream, or even something else? You cannot know. The first needs to be closed properly but the second doesn't need to.

So what? .close() them all anyway, you have nothing to lose. Do you really want to take the chance?

With Java 6, you'll want to use Guava's Closer, since this is the safest way to have all your resources closed (and the JDK does not provide such a tool):

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
}

With Java 7, you have try-with-resources which works with all AutoCloseable resources (which Closeable extends):

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

The main difference between Closer and try-with-resources is that with the latter, resources will be closed before catch whereas Closer will close them in finally.

But again: don't take a chance. Close them all.



回答3:

The answer depends on the stream.

A memory leak is simply a situation where you keep around things that you didn't intend to. This is always due to a programming error: someone somewhere forgot to release ("detach" from all strong references) some instances. These add up over time and then you notice the "leak". A cache that grows indefinitely is a good example.

A stream, for example, could place some data into a shared container (typically static) like a resource lock, some kind of cache, etc. as soon as it is open/used. And then it cleans up this resource in the close method. So skipping this last part does create a memory leak.

In your example the ByteArrayInputStream.close() method does nothing, so no problem there. BufferedInputStream.close() just delegates the call to the wrapped class so, in this case, again there is no problem.

More complex streams dealing with files, network streams can create leaks if not closed but it is not common. I'm assuming that the stream itself is not kept around but left available for collection. In many situations, a "smart" stream may even fix these oversights during its own collection and perform the necessary cleanup by itself (this remains an anomalous situation that the stream should clearly log/notify somewhere).