All issues follow the same bug pattern, as the code has been reused via copy and paste:
Properties props = new Properties();
props.load(new FileInputStream(file));
Why is this a problem? The FileInputStream closes the underlying file descriptor from the operating system in the finalize() method, right? It does indeed: eventually (see Section 12.6 of the Java Language Specification), and that's exactly the problem, especially in a server-like application. If the heap is large, the garbage collection may run infrequently; if the open rate is higher than the reclaim rate, the application may exhibit byzantine failures in the attempt to access OS resources. The stack trace won't help in this case, because it will show the resource allocation trace, not the where the leak occurred. Long story short: don't let this happen to you! The try-with-resource block solves this elegantly:
Properties props = new Properties();
try(FileInputStream fix = new FileInputStream(file)) {
props.load(new FileInputStream(file));
}
By following the call traces of the Properties.load method in OpenJDK 8, I counted 8 proper try-with-resources, 15 leaks, 13 leaks in the exception case, 18 instances of correct handling, and three instances of broken release-logic that end up releasing the file descriptor, but not other streams. In other words, almost half the call sites got it wrong. Luckily, the vast majority are in the CORBA, JAX-P, the repackaged aging xerces and tools modules, which aren't used as much. Unless your application uses XML, which is how I found it six years ago.
The instances I counted under "broken release logic" follow the bug pattern below (extracted from java.util.logging.LogManager):
try (final InputStream in = new FileInputStream(fname)) {
final BufferedInputStream bin = new BufferedInputStream(in);
readConfiguration(bin);
}
The precious FileInputStream is closed, but it's closed underneath the BufferedInputStream that wraps it, assuming that the latter doesn't use system resources itself. The correct way to code it is like this:
try (final BufferedInputStream bin = new BufferedInputStream(new FileInputStream(fname))) {
readConfiguration(bin);
}
Depending on how Oracle reacts to the new bug report, I may contribute more than just the JAX-P fixes.