11

I have a case where I need to create a lot of class loaders in my application to temporarily make some code visible while user supplied scripts are running. I'm using an URLClassLoader for this and it works pretty well.

When the script terminates, I want to "unload" or "close" the class loader to free the resources.

Is it enough to set the reference to the class loader to null? I'm especially wondering if I'll eventually run out of file handles because the extra classes are in JAR files.

PS: Must work with Java 5 and up. Yeah, I know...

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820

6 Answers6

9

A little late, but hopefully this'll be helpful for those who come to this Question later (like me).

With Java 7, a close() method has been added to URLClassLoader, which is exactly what OP was asking for.

EDIT (thanks to @Hot Licks): OK, so it isn't exactly what the OP has asked for. It doesn't free up all the resources, or make the resources and the loader collectible. It simply prevents the loading of more resources using the class loader. It does, however, close the jar file that was loaded with the URLClassLoader.

aspiring_sarge
  • 2,355
  • 1
  • 25
  • 32
  • The *close()* method does not make the loader & it's resources collectible, it simply prevents the loading of more classes. – Hot Licks Dec 05 '15 at 14:19
  • Thank you. Not sure how I missed that. It does, however, seem to close the jar file, which means that the jar file can now be deleted/modified, etc, if I understand correctly? – aspiring_sarge Dec 05 '15 at 15:27
  • Yes, it implies that jar files are closed. – Hot Licks Dec 05 '15 at 15:38
5

If you can't use Java7 and it's close() method, use reflection to close all open JAR archives of a classloader, like so:

public void close() {
try {
   Class clazz = java.net.URLClassLoader.class;
   java.lang.reflect.Field ucp = clazz.getDeclaredField("ucp");
   ucp.setAccessible(true);
   Object sun_misc_URLClassPath = ucp.get(this);
   java.lang.reflect.Field loaders = 
      sun_misc_URLClassPath.getClass().getDeclaredField("loaders");
   loaders.setAccessible(true);
   Object java_util_Collection = loaders.get(sun_misc_URLClassPath);
   for (Object sun_misc_URLClassPath_JarLoader :
        ((java.util.Collection) java_util_Collection).toArray()) {
      try {
         java.lang.reflect.Field loader = 
            sun_misc_URLClassPath_JarLoader.getClass().getDeclaredField("jar");
         loader.setAccessible(true);
         Object java_util_jar_JarFile = 
            loader.get(sun_misc_URLClassPath_JarLoader);
         ((java.util.jar.JarFile) java_util_jar_JarFile).close();
      } catch (Throwable t) {
         // if we got this far, this is probably not a JAR loader so skip it
      }
   }
} catch (Throwable t) {
   // probably not a SUN VM
}
return;
}
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
chaity
  • 163
  • 2
  • 11
  • I'm using java 1.6, I forced full GC to clean up URLClassloader and I still saw many FD of jar files shown by `lsof`. You code solved my problem! – waltersu Mar 20 '17 at 11:28
4

When all classes that the class loader loaded no longer have any references, and all references to the class loader itself have been erased, the class loader and the classes it loaded will be garbage collected as a group.

Note that this is dependent on having the JVM attribute set that causes unreferenced classes to be unloaded. It's set by default in most environments, but may not be in some embedded cases.

[Note that it's a non-trivial matter to remove references to a class. Any other class that references it by name will of course prevent removal. So the class must be loaded using ClassLoader.findClass or something similar.]

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
  • No. It's one of the more obscure ones, and may not be externalized on the later JVMs. – Hot Licks Sep 20 '11 at 17:01
  • That is one side of the coin. If I change the JAR which contains the classes, create a new classloader and load via the exact same URL, will I get the new classes or is there caching involved? – Aaron Digulla Sep 21 '11 at 12:39
  • To my understanding, each class loader has its own resolution path, so you can search for the same class in two different class loaders and get two different classes. There is a check of sorts that certain classes (I'm thinking those with native methods) can only be loaded once by one class loader, but this restriction doesn't apply to "normal" classes. (However, this whole area is one where Sun made repeated changes in the past, and my knowledge is about 3 years ancient.) – Hot Licks Sep 21 '11 at 15:30
  • Indeed they are different classes, even if they share the same name. An object of type MyClass from classloader A cannot even be cast to the type of MyClass from classloader B. This is one of the things that can make Groovy handy to work with: dynamic variable types and method calls work regardless of an object's true class. – Corrodias Mar 04 '21 at 21:23
3

If you do not longer have classes (and object) loaded from that classloader, and if you do not keep any reference to that classloader, it will automatically handled by the garbage collector.

Matteo
  • 1,367
  • 7
  • 25
1

There are no close() methods in URL class loader or any of its parent classes, so you're out of luck.

Shouldn't GC handle this?

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • Agree, just stop referencing your ClassLoader (from its parent especially) and it will be collected, its classes will follow. – Guillaume Sep 20 '11 at 15:52
-1

I extended URLClassLoader and made a close method based on Java 7s. I wanted to develop my IRC bot on my iPad 2, so I did what was needed. Now my plugin system is stable on Java 6 and 7, hurray.

Callum
  • 7
  • 1