I have three questions.
To explain, I was reviewing someone’s code, and noticed BufferedReaders sometimes aren’t being closed. Usually, Eclipse gives a warning that this is a potential memory leak (and I fix it). However, within a Callable inner class, there is no warning.
class outerClass {
...
public void someMethod() {
Future<Integer> future = outputThreadPool.submit(new innerClass(this.myProcess.getInputStream(), threadName));
...
}
class innerClass implements Callable<Integer> {
private final InputStream stream;
private final String prepend;
innerClass(InputStream stream, String prepend) {
this.stream = stream;
this.prepend = prepend;
}
@Override
public Integer call() {
BufferedReader stdOut = new BufferedReader(new InputStreamReader(stream));
String output = null;
try {
while ((output = stdOut.readLine()) != null) {
log.info("[" + prepend + "] " + output);
}
} catch (IOException ignore) {
// I have no idea why we're ignoring this... :-|
}
return 0;
}
}
}
The people who wrote the code are experienced Java developers, so my first thought is that it’s intentional… but it could be they were in a hurry when they wrote it and just overlooked it.
My questions are:
-
Why does Eclipse not highlight this (which may be answered by the answer to the following questions)?
-
What is the worst that could happen if it’s closed within the call() method? (I can’t think of a good reason… and I’ve been searching for a while… but maybe it was intentional not to close the BufferedReader)
-
What is the worst that could happen if the BufferedReader is not closed within the inner class?
I would say that since they’re creating a
BufferedReaderaround a givenInputStream, the code is safe not callingclose(). The code that callsclose()should always be the code that creates the stream and done using try/finally.Another note: your code appears to be logging output from some other process. You probably wouldn’t be able to close the stream anyway. Without testing it myself, I’m not sure what would happen if you closed a stream from another process.
Oh, and the
IOExceptionisn’t likely to happen because the stream is coming from another process. That’s not likely to happen unless some irrecoverable error occurs. It still wouldn’t be a bad idea to log the exception somehow, though.Edit to address your comment about mixed answers:
Let’s use an output stream and
BufferedWriteras an example this time:This works. The writeLine method is used as a delegate to creating
writerand actually writing a singlelineto the file. Of course, this logic could be something more complex, such as turning an object into aStringand writing it. This makes themainmethod a little easier to read too.Now, what if instead, we closed the BufferedWriter?
Try running it with that, and it will fail every time on the second
writeLinecall. It’s good practice to always close streams where they’re created, instead of where they’re passed. It may be fine initially, but then trying to change that code later could result in errors. If I started with only 1writeLinecall with the bad method and someone else wanted to add a second one, they’d have to refactor the code so thatwriteLinedidn’t close the stream anyway. Getting close-happy can cause headaches down the road.Also note that technically, the
BufferedWriterisn’t the actual handle to your system resource, theFileOutputStreamis, so you should be closing on the actual resource anyway.So, rule of thumb: only close your streams where you create them, and always do the creation and closing in a try/finally block (or Java 7’s awesome try/resource block, which does the closing for you).