Does Input/OutputStreams close on destruction?

2020-02-10 04:22发布

问题:

Does InputStreams and OutputStreams in Java close() on destruction? I fully understand that this may be bad form (esp in C and C++ world), but I'm curious.

Also, suppose I have the following code:

private void foo()
{
    final string file = "bar.txt";
    Properties p = new Properties();
    p.load( new FileInputStream(file) );
    //...
}

Does the nameless FileInputStream goes out of scope after p.load(), and therefore get destroyed, kinda like C++ scoping rules? I tried searching for anonymous variable scope for java on Google, but that didn't turn up what I thought it would be.

Thanks.

回答1:

First answer: there's no such thing as "destruction" (in the C++ sense) in Java. There's only the Garbage Collector, which may or may not wake up and do its job when it sees an object that's ready to be collected. GC in Java is generally untrustworthy.

Second answer: sometimes yes, sometimes no, but not worth taking a risk over. From Elliote Rusty Harold's Java IO:

Not all streams need to be closed—byte array output streams do not need to be closed, for example. However, streams associated with files and network connections should always be closed when you're done with them. For example, if you open a file for writing and neglect to close it when you're through, then other processes may be blocked from reading or writing to that file.

According to Harold, the same goes for Input or Output streams. There are some exceptions (he notes System.in), but in general, you're taking a risk if you don't close file streams when you're done. And close them in a finally block, to make sure they get closed even if an exception is thrown.



回答2:

I used to assume streams would be closed automatically eventually via garbage collection, but anecdotal evidence indicates that failure to manually close them results in a resource leak. You will want to do something like this instead:

InputStream stream = null;

try {
  stream = new FileInputStream("bar.txt");
  Properties p = new Properties();
  p.load(stream);
}
catch(Exception e) {
  // error handling
}
finally {
  closeQuietly(stream);
}

closeQuietly() is a method on IOUtils in Apache's commons-io library.



回答3:

No, there are no destructors in Java. There may be other references to the object, even after one particular reference to it goes out of scope (or is modified). If the object is no longer reachable, the stream may have its finaliser called sometime later which will close the stream.

Properties.load is peculiar in the it closes the stream passed to it. Edit: Properties.loadFromXML is the special method I appear to have been thinking about five or so years ago. (API doc should probably say before rather than after.) Thank you @tzimnoch.



回答4:

The variable goes out of scope and is therefore destroyed. But in Java, there is a very big distinction between a variable and the object which the variable points to.

The object pointed to is not destroyed merely when the variable goes out of scope. The object is only destroyed when the Java runtime engine decides it feels like getting around to destroying objects that are not pointed to by any in-scope variables.



回答5:

The short answer is "maybe, but don't bet on it!".

Somewhere in the stack of classes that implement FileInputStream is a class that has a finalizer that will effectively close the stream (and release the resource) when it is run.

The problem is that there is no guarantee that the finalizer will ever run. Quoting from the JLS (section 12.6):

The Java programming language does not specify how soon a finalizer will be invoked, except to say that it will happen before the storage for the object is reused.

This makes stream finalization problematic:

  1. If your Stream object never becomes garbage, it will never be finalized.
  2. If your Stream object is tenured, it may take a long time before it is garbage collected and finalized.
  3. The JVM may need to perform an extra GC cycle after the object is identified as unreachable before the finalizer is executed. This is certainly allowed by the JLS!
  4. It is technically legal for a JVM to never, ever to execute finalizers, provided that it never reuses the store of objects with finalizers. (I'm not aware of any production quality JVMs that take this line, but you never know ...)


标签: java scope