views:

8471

answers:

6

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

+3  A: 

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
+5  A: 

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
+16  A: 

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.

http://java.sun.com/docs/books/tutorial/deployment/jar/jarclassloader.html

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.

Mario Ortegón
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.
Dan
A: 

Yes there are ways to load classes and to "unload" them later on. The trick is to implement an 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 it's package, it's 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 brake 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 an other classloader than the system classloader).
  • For every class that shall be replaceble instantiate an own classloader and load it through this classloader (which really loads the class and does not delegate it to the parent classloader)
  • Remember 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 of a class that was loaded before it returns the class from the class loader stored before.
  • It should be enough to refind 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 http://www.manning.com/neward3/ - that book helped me implementing something very similar to what you want.

Georgi
+1  A: 

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
A: 

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