endorphins
endorphins

Reputation: 606

Should an OutputStream object (created as an expression in method call, but not assigned to a variable) still be closed?

I'm trying to use a FileOutputStream anonymously for storing a property with java.util.Property's store method.

Properties p = new Properties();
. . .
p.store(new FileOutputStream("nameOfFile"), "Created by me.");

Reading the javadocs for the store method it says that the stream will be flushed and remain open after the method returns.

I'm wondering what happens with the stream. Since I've instantiated it anonymously I'm under the assumption that the object will be garbage collected immediately (if that is wrong, please correct me). However, I was taught to always close my streams when I'm finished with them. In this case, I am unable to close the stream since I have no object referring to it. Do I need to be worried about the stream being left open here, or does Java take care of it appropriately in this case by immediately collecting the object?

Upvotes: 4

Views: 970

Answers (3)

M A
M A

Reputation: 72884

Garbage collection generally does not include resources like open files or streams, unless the finalize method explicitly closes the underlying resources of the object.

Checking the code of finalize for FileOutputStream, it seems to make sure the close is called:

/**
 * Cleans up the connection to the file, and ensures that the
 * <code>close</code> method of this file output stream is
 * called when there are no more references to this stream.
 *
 * @exception  IOException  if an I/O error occurs.
 * @see        java.io.FileInputStream#close()
 */
protected void finalize() throws IOException {
    if (fd != null) {
        if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
            flush();
        } else {

            /*
             * Finalizer should not release the FileDescriptor if another
             * stream is still using it. If the user directly invokes
             * close() then the FileDescriptor is also released.
             */
            runningFinalize.set(Boolean.TRUE);
            try {
                close();
            } finally {
                runningFinalize.set(Boolean.FALSE);
            }
        }
    }
}

However, it's always better to always close the resource manually, or in a try-with-resources statement, because not all stream objects may close upon being de-referenced, and even if they do finalization is not guaranteed to occur according to expectations:

try(FileOutputStream outStream = new FileOutputStream("nameOfFile")) {
     Properties p = new Properties();
     . . .
     p.store(outStream, "Created by me.");
}

Upvotes: 2

Andy Brown
Andy Brown

Reputation: 19171

That's not an anonymous object (or rather an anonymous class). It's just an unassigned object instantiation.

Once you create it and pass it to store there is a reference to it, therefore it is not GC'd. Once store returns that reference disappears, but you should still track the variable and dispose of it after store returns. It is safer to just to treat all resources in the same standard way and call close when you are done with them, this helps avoid a lot of resource leak errors when some finalize methods call close ad other's don't, and also avoids issues due to non-deterministic GC.

You also don't know what may change in a finalizer implementation in a future version so always calling close is best practice.

A try-with-resources block (Java Tutorials > The try-with-resources statement) is ideal for this:

Properties p = new Properties();

try (FileOutputStream stm = new FileOutputStream("nameOfFile"))
{
    p.store(stm, "Created by me.");     
} catch (IOException e) {
    // TODO: handle this
}

That block, when used with any (and potentially multiple) Autocloseable, will call close at the end and nicely handle things like exceptions on close or exceptions in the body and on close as well. If both the body and close throw exceptions then the body exception is rethrown and the close exception is available through e.getSuppressed().

You should not call Autocloseable.close() yourself if you use one of these blocks as that method is not indempotent - calling it twice may have side effects the second time. If you have a Closeable then you are safe to call it multiple times. From the docs:

Note that unlike the close method of Closeable, this close method is not required to be idempotent

In Java 6 or less you have to do your own exception handling statement which is hard as you have to handle the case where both your body, and the close both throw exceptions.

Upvotes: 4

Aninda Bhattacharyya
Aninda Bhattacharyya

Reputation: 1251

Create an object...

FileOutputStream fos = new FileOutputStream("nameOfFile")

And then use it...

p.store(fos, "Created by me.");

Close it on finally.

The Properties.store() command does not keep a reference to the stream hence there are no references to it and hence it will be GC'ed after the method returns as that is the extent of the streams scope.

Upvotes: 0

Related Questions