Search code examples
javastreaminputstreamoutputstream

Do streams that are not instantiated into an object close themselves on return?


I am learning Java and have a question that I can't seem to find the answer to anywhere. Or maybe I just lack the proper keywords to google it, as I'm relatively new to Java.

I'm trying to create a class for deep-copying using Serialization.

public class SerialDeepCopier<T extends Serializable> {
    public T deepCopy(T origin) throws Exception{

        ByteArrayOutputStream writeBuffer = new ByteArrayOutputStream();
        ObjectOutputStream oStream = new ObjectOutputStream(writeBuffer);

        oStream.writeObject(origin);

        ByteArrayInputStream readBuffer = new ByteArrayInputStream(writeBuffer.toByteArray());
        ObjectInputStream iStream = new ObjectInputStream(readBuffer);


        T returnValue = (T) iStream.readObject();

        /*
        CLOSE THE STREAMS
        */
        writeBuffer.close();
        oStream.close();
        readBuffer.close();
        iStream.close();

        return returnValue;
    }
}

I'm aware that it's good practice to always close streams after the program is done using them, which begs this question because not instantiating the streams into an object also works:

ByteArrayOutputStream writeBuffer = new ByteArrayOutputStream();
new ObjectOutputStream(writeBuffer).writeObject(origin);

When I wrote it like that, I obviously can't manually call the close() method to close the stream (or at least I don't know how to) as there is no object to do it from.

So, if I instantiate a stream without assigning it to an object, how am I supposed to close it?

  • Does the GC just automagically closes it?
  • Does it automatically close on return?
  • Or does it just immediately closes after instantiation and completing it's writeObject(origin) task?

Solution

  • No. They do not close themselves on return.

    1. Does the GC just automagically closes it?

    Eventually. If the GC detects that a stream is unreachable it will trigger its closure via finalization or a cleaner.

    1. Does it automatically close on return?

    No. Until the GC runs, the JVM doesn't know if the stream is still reachable. A reference to the stream could have been squirreled away in (say) a static variable by various of the constructors or methods called in your deepCopy method.

    1. Or does it just immediately closes after instantiation and completing it's writeObject(origin) task.

    No. The streams objects themselves have no way of knowing that the writeObject call is the last task that will be performed on / with then.


    So is it just generally a bad idea to do this: new ObjectOutputStream(writeBuffer).writeObject(origin); ?

    It depends what the underlying streams are.

    • In this case, the streams are reading and writing in-memory byte arrays. So in this case it doesn't really matter that they are not garbage collected quickly.

    • On the other hand, if the streams were connected to external resources (e.g. files, pipes, sockets, etc) then it can matter. For instance on Windows you may be holding a lock on a file that prevents other applications opening it. Or on Linux, you are tying down file descriptors ... which are a relatively scarce resource.

    Having said that, it is a good idea to close streams anyway:

    1. The cost of an unnecessary close is minuscule. (The benefit is piece of mind.)
    2. Skipping a necessary close could lead to resource leakage problems that then lead to application errors; e.g. file or socket open failures, exec failures, and so on.
    3. Some output streams require a close() to complete the stream. A flush() is not always sufficient. (An example is an output stream that does compression or encryption.)
    4. Some code analysis tools will give you warnings about streams that are not closed ... even in cases where it doesn't matter.

    And the simplest reliable way to do it is to use try with resources.