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.