JDK-8013099 : (sl) ServiceLoader interferes with URLClassLoader.close()
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util
  • Affected Version: 7,8
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • Submitted: 2013-04-24
  • Updated: 2018-07-17
  • Resolved: 2018-07-17
Related Reports
Blocks :  
Relates :  
Relates :  
Relates :  
The intent of URLClassLoader.close() is to release all resources opened by the loader, such that any jar files referenced by the URLClassLoader can be deleted.  By itself, URLClassLoader now does that, but if you use a URLClassLoader with ServiceLoader, then the ServiceLoader will open additional handles onto any jar files, such that on Windows you can no longer delete jar files after calling URLClassLoader.close.

This is because URLClassLoader uses private instances of JarFile to access jar files, but ServiceLoader uses ClassLoader.getResources to open service config files, which returns URLs, and ServiceLoader then uses url.openInputStream() which creates and uses instance of URLJarFile, which are /not/ closed when URLClassLoader.close is called.

URLClassLoader (actually URLClassPath) and ServiceLoader need to coordinate on the handle used to access jar files, such that URLClassLoader can honor its contract to release resources when URLClassLoader.close is called.
This issue is JDK 8 and older. There are no current plans to backport the changes to disable the JAR file caching in the older releases so closing this bug.

I've added 9-na to this bug because we believe it is no longer applicable to JDK 9. In JDK 9 then the SL does not use the jar caching and it also ensures that the input stream to the services configuration file is closed. JDK-8156014 is another issue that seems to be the same thing. In that issue then the submitter has confirmed that they do not see the issue with JDK 9.

Added modularization label to get this on the JIgsaw radar, because this blocks JDK-8062324

I think we're agreed the fix isn't in j.n.URLClassLoader. So, I'm changing subcat to java,util.

The proposed patch could potentially introduce a performance issue to many usages of ServiceLoader in the platform. So some performance analysis will be likely be required here.

Michael, The point is about URLClassLoader keeping track for streams it hands out. This is about the way that ServiceLoader interacts with URLClassLoader. Your first comment (2013-04-24) was noteworthy in describing the problem and suggesting a solution. The fix needs to be in ServiceLoader, in that ServiceLoader needs to disable caching in URLClassLoader. Here is the relevant workaround we are using in a clone of the ServiceLoader code, in ServiceLoader.parse: try { // The problem is that by default, streams opened with // u.openInputStream use a cached reference to a JarFile, which // is separate from the reference used by URLClassLoader, and // which is not closed by URLClassLoader.close(). // The workaround is to disable caching for this specific jar file, // so that the reference to the jar file can be closed when the // file has been read. // Original code: // in = u.openStream(); // Workaround ... URLConnection uc = u.openConnection(); uc.setUseCaches(false); in = uc.getInputStream(); // ... end of workaround. try (BufferedReader r = new BufferedReader(new InputStreamReader(in, "utf-8"))) { int lc = 1; while ((lc = parseLine(service, u, r, lc, names)) >= 0); } } catch (IOException x) { fail(service, "Error reading configuration file", x); } finally { try { if (in != null) in.close(); } catch (IOException y) { fail(service, "Error closing configuration file", y); } } Look for these lines in particular: // Original code: // in = u.openStream(); // Workaround ... URLConnection uc = u.openConnection(); uc.setUseCaches(false); in = uc.getInputStream(); // ... end of workaround.

Reopening for clarification, since it may have been closed incorrectly.

as per final comment

I'm going to close this as not a bug as URLClassLoader can't keep track of resources opened by APIs that it does not control. Code that acquires InputStreams form URLs must take care to close these streams themselves.

FWIW, the suggested workaround works well in a sandbox repo.

Not clear (yet) if this is a bug. URLClassLoader returns jar: URLs from getResource() off a jar file. Opening these URLs returns JarURLConnections, which have always implemented caching behavior such that when caching enabled, the underlying JAR file is kept open for further access. This caching can be switched off. So, instead of open a URL and getting a stream using URL.openStream(), maybe ServiceLoader could get a URLConnection first and then disable caching before getting the stream.