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
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.
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.
I wrote a custom classloader, from which it is possible to unload individual classes without GCing the classloader. Jar Class Loader
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,
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.
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.
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.
Related
I have built in my application the ability to reload classes. However, I need to clarification on the behaviour of the class loader.
Let me explain what I know and then ask the questions...
What I do is provide a special jar that is loaded by a custom classloader. Then during the bootstrap of the jar a bunch of spring beans are created and the ones which implement certain interfaces get registered for use by the central app.
Now I kick off a process in the application which uses these new classes. I can successfully unload the "jar", change classes in the jar and reload and I get the changes. Unloading in class loader parlance means making the class loader that loaded the classes unreachable - this causes any classes loaded by that class loader to be unreachable and thus effectively unloaded.
However, from what I know so far, once the vm has loaded the class it stores it in some shared space so does not have to load it again.
The issue that I have is that sometimes the unload and reload of a new class does not work. The old class stays around. It stands to reason that if I unload a class loader (i.e. make the class loader unreachable) and one of the classes in that class loader is currently in use (an object of that type exists) then the class cannot be unloaded.
Is this true? It seems to be true in practice.
If that is so, how do I successfully unload classes that are in use at the time the class loader goes unreachable. Could I for example, put a weak reference on each class so I can detect when the class goes unreachable and act when the class goes unreachable? (not sure what action I could take though).
Update in response to #Kayaman
My use case is that I have the core application and then based on the customers requirements I can load different classes that implement known interfaces in the core application (so they can be accessed). Then the core application kicks off various processes which use these classes. The big strength of this is that I can update these plugin classes without doing a big redeploy and every customer does not need every one of these. The problem comes in when I want to load a new version of one of these and the current version is in use. Kinda stands to reason that this is not possible.
Conclusion
#Kayamam many thanks for your consult. It has been very helpful. This has codified my thinking somewhat. The conclusion is that no matter what technique I use you cannot ever, with the VM the way it is, reload a class for which there is currently a strongly reachable object. For some of my reloads I have control over these objects as I can make them unreachable before I unload and reload but for other classes I cannot do this... that is where my problem lies. What I need to do is to ring fence the objects of the classes that I wish to reload so that I can the process that is using them to pause while I reload the classes for those objects.
As you said, in order to unload a class, you need to get rid of the classloader. For example URLClassLoader can be used to load classes, and then null out the reference to make it eligible for GC and therefore unload the classes it loaded.
However, all classes know which classloader loaded them. That means that if you've got instances of your classes in use, they have a reference to the Class which has a reference to the ClassLoader and will prevent it from being collected and classes being unloaded. This is understandable, since having an object without a class would be quite an interesting situation.
So for a full reload you need to get rid of old instances and get rid of the classloader. That also avoids situations where you get mysterious exceptions about MyClass != MyClass.
WeakReference (or PhantomReference would probably be better here) would allow you to notice when your existing objects get collected, you just need to make sure you're tracking them all.
Spring adds to the complexity here, so I strongly recommend spending some time imagining that this approach is impossible, and to see if Spring has something that could be used to fulfil your business requirement. After all it does a lot of classloading itself, so you might be reinventing the wheel in a less clear way.
With quick Googling I found this http://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html which mentions among other things Spring Loaded which can apparently do class reloading and then some.
As we know java class loading works like in picture below.
When we have a notion of plugin in our application (like app servers) we sometimes need some classes to be loaded in the instances class loader rather than in parent, so that each new instance (plugin, webapp or whatever) loads that particular class not delegating the parent...
For example Log4j classes, we need them to be loaded for each instance.
So in order to do this the best approach that came into my mind is to write custom classloader that will take a list of class names which shall be prevented from being delegated to parent classloader (ideally we want instances to be in complete isolation).
So the the application that will load other "instances" will use that custom classloader while loading those "instances"...
Is there an implementation of such classloader that solves this issue? (given that we dont want to know what OSGi is and we work with pure java no frameworks etc...)
My searches end up pointing some frameworks or some web container specific solutions, yet this is quite a simple task that is solved with one class, I'd like to find out if i'm missing something before i start implementing it.
Update (digging deeper) : suppose there is a class loaded by bootstrap which has static state that can be shared between instances (and we really badly want to make sure instances are completely isolated), now that class is obviously not included in our classpath, but it is loaded, and if we copy it instead of reffering it we will achieve the required isolation.
So do we have the notion of copying or cloneing a class from one classloader to other?
I have a question about java ClassLoaders. I am trying to use different ClassLoaders to be able to run different versions of a JAR from within the same program.
I have heard somewhere that if you load one class using one ClassLoader all classes called (being loaded) from within that class would use the same ClassLoader. Is this correct?
If not, is there a neat way to set the context of a ClassLoader (let's say, everything being called from a specific class/library should use the same ClassLoader).
This is not a simple subject and i would advise doing more research online as no answer here will be nearly in depth enough. but, as a quick synopsis:
classes loaded via normal class references (i.e. a line of code in Class A which uses a variable of static type B) will be loaded using the same classloader as the initial class.
however, due to classloader delegation, a class may not actually be loaded by the ClassLoader from which the search originally started. example, i have Class A loaded by classloader LA with parent classloader LP. Class B is referenced by A, so the search for Class B will start with LA. however, the class bytes for B are actually found in LP, so LP loads the class and hands it to LA which returns it. ultimately, however, B is owned by LP, not LA.
with utilities which load classes via reflection (e.g. serialization, JAXB, Hibernate, etc.) or frameworks which are typically used with nested classloaders (e.g. Java EE appservers), all bets are off. typically utilities/frameworks like this load classes using the context classloader, but that is not always the case. each utility may have different priorities and fallbacks regarding which classloader is used. additionally, many have ways of explicitly providing a classloader at runtime.
as a rule of thumb, while executing code which you know is from a nested classloader (probably because you set it up), you should set the current context classloader appropriately.
If I have two webapps, both of which have the same third party library jars in their web-inf/lib directories, say log4j.....when the first webapp is loaded and a log4j class is created, this class is loaded into the heap. When the second webapp is loaded and tries to load a log4j class, will it find the class in the heap and use that one? Or will it load its own copy of the class into the heap?
Hmm I think it is mostly a ClassLoader problem here, it still depend of the application server you are using, but I guess most of them use a single JVM and reserve a ClassLoader per running webapp so that you can have different webapp with different version of the same jar/clases running alltogether.
In tomcat for instance, if you need to have some shared libraries, you can use the /tomcat/shared/lib folder where you put all the jars that will be accessible by all your webapps.
Otherwise yes, different webapp won't share the same heap, it would mean webapp could access object created by other webapp running in the same application server
No, it shouldn't.
The fact classes are loaded on the heap mean nothing here. as each class loader maintains its own list of classes it loaded.
However, Classloaders are also organised into a tree and they are supposed to ask their parent classloaders to attempt to load the class first, as described in the javadoc of the ClassLoader class.
The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself.
However, webservers typically don't follow this delegation model, to avoid libraries used by the webserver itself being picked up by webapps. (This behaviour is sometimes configurable, but it depends on the webserver you're using.)
So in practice every webapp should have its own separate class space, independent of all the other webapps, therefore they can even use two different versions of the same library without any problems.
The other important lesson is that the same class file loaded by two different class loader will actually be two separate classes in the heap and objects from one class will not be compatible with the other class.
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
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.
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.
I wrote a custom classloader, from which it is possible to unload individual classes without GCing the classloader. Jar Class Loader
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,
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.
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.
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.