195

I have a custom class loader so that a desktop application can dynamically start loading classes from an AppServer I need to talk to. We did this since the amount of jars that are required to do this are ridiculous (if we wanted to ship them). We also have version problems if we don't load the classes dynamically at run time from the AppServer library.

Now, I just hit a problem where I need to talk to two different AppServers and found that depending on whose classes I load first I might break badly... Is there any way to force the unloading of the class without actually killing the JVM?

Hope this makes sense

Alex Miller
  • 69,183
  • 25
  • 122
  • 167
el_eduardo
  • 3,138
  • 4
  • 21
  • 17
  • Do you have a classloader for each jar? How does OSGI containers archives to unload classloader? It looks there's no unload API in the classloader class? – hetaoblog Aug 12 '11 at 08:36

7 Answers7

206

The only way that a Class can be unloaded is if the Classloader used is garbage collected. This means, references to every single class and to the classloader itself need to go the way of the dodo.

One possible solution to your problem is to have a Classloader for every jar file, and a Classloader for each of the AppServers that delegates the actual loading of classes to specific Jar classloaders. That way, you can point to different versions of the jar file for every App server.

This is not trivial, though. The OSGi platform strives to do just this, as each bundle has a different classloader and dependencies are resolved by the platform. Maybe a good solution would be to take a look at it.

If you don't want to use OSGI, one possible implementation could be to use one instance of JarClassloader class for every JAR file.

And create a new, MultiClassloader class that extends Classloader. This class internally would have an array (or List) of JarClassloaders, and in the defineClass() method would iterate through all the internal classloaders until a definition can be found, or a NoClassDefFoundException is thrown. A couple of accessor methods can be provided to add new JarClassloaders to the class. There is several possible implementations on the net for a MultiClassLoader, so you might not even need to write your own.

If you instanciate a MultiClassloader for every connection to the server, in principle it is possible that every server uses a different version of the same class.

I've used the MultiClassloader idea in a project, where classes that contained user-defined scripts had to be loaded and unloaded from memory and it worked quite well.

RevanthKrishnaKumar V.
  • 1,855
  • 1
  • 21
  • 34
Mario Ortegón
  • 18,670
  • 17
  • 71
  • 81
  • 34
    Note also that according to http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html unloading of classes is an optimization and, depending on the JVM implementation, may or may not actually occur. –  Feb 18 '10 at 11:50
  • 5
    As an easier and lightweight alternative to OSGi, try [JBoss Modules](https://docs.jboss.org/author/display/MODULES/Home) - modularized classloading with classloader per module (a group of jars). – Ondra Žižka Jun 26 '13 at 18:35
49

Yes there are ways to load classes and to "unload" them later on. The trick is to implement your own classloader which resides between high level class loader (the System class loader) and the class loaders of the app server(s), and to hope that the app server's class loaders do delegate the classloading to the upper loaders.

A class is defined by its package, its name, and the class loader it originally loaded. Program a "proxy" classloader which is the first that is loaded when starting the JVM. Workflow:

  • The program starts and the real "main"-class is loaded by this proxy classloader.
  • Every class that then is normally loaded (i.e. not through another classloader implementation which could break the hierarchy) will be delegated to this class loader.
  • The proxy classloader delegates java.x and sun.x to the system classloader (these must not be loaded through any other classloader than the system classloader).
  • For every class that is replaceable, instantiate a classloader (which really loads the class and does not delegate it to the parent classloader) and load it through this.
  • Store the package/name of the classes as keys and the classloader as values in a data structure (i.e. Hashmap).
  • Every time the proxy classloader gets a request for a class that was loaded before, it returns the class from the class loader stored before.
  • It should be enough to locate the byte array of a class by your class loader (or to "delete" the key/value pair from your data structure) and reload the class in case you want to change it.

Done right there should not come a ClassCastException or LinkageError etc.

For more informations about class loader hierarchies (yes, that's exactly what you are implementing here ;- ) look at "Server-Based Java Programming" by Ted Neward - that book helped me implementing something very similar to what you want.

Sled
  • 18,541
  • 27
  • 119
  • 168
Georgi
  • 4,402
  • 4
  • 24
  • 20
  • 4
    I don't understand people, who ticked -1 for this answer without leaving a comment. For me looks good. Perhaps having one `ClassLoader` per class is a bit too much, one `ClassLoader` per JAR makes sense. Could be be more specific about how to force class upload in proposed schema? E.g. how can I guarantee, that instances of classes loaded by ClassLoaderA are not referred by instances loaded by ClassLoaderB? – dma_k Jan 20 '11 at 00:23
  • 1
    @dma_k exactly, the answer is good, but it is not touching the key points you mentioned. – zinking Oct 21 '13 at 10:03
  • @Georgi is there an existing implementation we can instaniate/reuse for this? – Sled Aug 02 '17 at 14:20
  • 1
    It would be very very helpful,if you could provide the sample java code.To be precise,i am looking for How to unload classes using CustomClassLoader but had no luck. – Sriharsha g.r.v Dec 13 '17 at 13:59
  • @Sriharshag.r.v Have you tried this and implemented a sample? – niaomingjian Mar 02 '18 at 01:57
18

I wrote a custom classloader, from which it is possible to unload individual classes without GCing the classloader. Jar Class Loader

Kamran
  • 829
  • 1
  • 10
  • 12
  • Works like a charm :). Is there any method to unload all class files of a jar file? – Ercksen Oct 13 '15 at 11:35
  • Unfortunately not at the moment. But will look into to it. May be in the future releases. – Kamran Nov 30 '15 at 17:42
  • By the way, I found a little workaround. If you have one `JarClassLoader` for every jar file you loaded, you can call `getLoadedClasses()` on it, then iterate over each and unload it. – Ercksen Nov 30 '15 at 17:54
13

Classloaders can be a tricky problem. You can especially run into problems if you're using multiple classloaders and don't have their interactions clearly and rigorously defined. I think in order to actually be able to unload a class youlre going go have to remove all references to any classes(and their instances) you're trying to unload.

Most people needing to do this type of thing end up using OSGi. OSGi is really powerful and surprisingly lightweight and easy to use,

Steve g
  • 2,471
  • 17
  • 16
7

You can unload a ClassLoader but you cannot unload specific classes. More specifically you cannot unload classes created in a ClassLoader that's not under your control.

If possible, I suggest using your own ClassLoader so you can unload.

Jason Cohen
  • 81,399
  • 26
  • 107
  • 114
4

Classes have an implicit strong reference to their ClassLoader instance, and vice versa. They are garbage collected as with Java objects. Without hitting the tools interface or similar, you can't remove individual classes.

As ever you can get memory leaks. Any strong reference to one of your classes or class loader will leak the whole thing. This occurs with the Sun implementations of ThreadLocal, java.sql.DriverManager and java.beans, for instance.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
-3

If you're live watching if unloading class worked in JConsole or something, try also adding java.lang.System.gc() at the end of your class unloading logic. It explicitly triggers Garbage Collector.

  • 4
    Careful: System.gc() don't necessary call GC. It only asks the jvm to start it, but it doesn't enforce it. And IME it often don't start the GC :-\ – Juh_ Jan 12 '18 at 08:23