I have two classes (A
and B
) which are loaded by different ClassLoaders
. Furthermore, I have a third class, which providers static getter and setter methods. I hope following picture can clarify the situation:
The Data
class looks as following:
public class Data { private static String data = "<fill in>"; public static void setData(String d) { data = d; } public static String getData() { return data; } }
In class A
, I want to set the static value of Data
and in B
I want to retrieve this value. However, in B
I always get the original value (which is "<fill in>"
). I only have a basic understanding of ClassLoader
s, so I’m not too sure what is going on under the hood. I thought that both ClassLoaders (clA
and clB
) will propagate to their parent ClassLoader
and that I will get the same Data
class in both. Can anyone give me some feedback on the behavior or point me in the direction to look at?
Update
When I print the hashCode()
of both Data
classes, I get different values for them (meaning obviously I don’t get access the same class). Is there and easy way to illustrate the ClassLoader
hierarchy?
Advertisement
Answer
If your question is how to illustrate or visualize the classloader hierarchy for objects, then you can walk up each classes classloader in code. You mentioned that you are using groovy, so an example would look like:
def showObjectClassLoaderHierarchy(Object obj) { def classLoader = showClassLoaderHierarchy(obj.getClass().getClassLoader()); showClassLoaderHierarchy(classLoader); } def showClassLoaderHierarchy(ClassLoader loader) { if (loader != null) { println "Classloader: " + loader.hashCode(); while (loader.getParent() != null) { loader = loader.getParent(); println " Child of: " + loader.hashCode(); } } }
I think you will find, in your code, the two Data objects are actually not loaded from the same classloader, which is why they have different static variables.
I put together a sample that has
- Main (loaded from parent classloader)
- DataObj with a static String (loaded also from parent classloader)
- LoadA, which instantiates a copy of DataObj (loaded from child classloader A)
- LoadB, which instantiates a copy of DataObj (loaded from child classloader B)
I see that while LoadA and LoadB have different classloaders, the DataObj and the static variable come from a common classloader.
Full code at: https://github.com/lucasmcgregor/groovy_classloader_test
The Main object in groovy:
import java.lang.ClassLoader; import java.net.URLClassLoader; import java.net.URL; def showObjectClassLoaderHierarchy(Object obj) { def classLoader = showClassLoaderHierarchy(obj.getClass().getClassLoader()); showClassLoaderHierarchy(classLoader); } def showClassLoaderHierarchy(ClassLoader loader) { if (loader != null) { println "Classloader: " + loader.hashCode(); while (loader.getParent() != null) { loader = loader.getParent(); println " Child of: " + loader.hashCode(); } } } println "Setting up child classLoaders A and B..."; def URL[] urlsA = [new URL("file:///tmp/cla/")]; def classLoaderA = new URLClassLoader(urlsA, this.getClass().getClassLoader()); def URL[] urlsB = [new URL("file:///tmp/clb/")]; def classLoaderB = new URLClassLoader(urlsB, this.getClass().getClassLoader()); println "Classloader A heirachry:"; showClassLoaderHierarchy(classLoaderA); println "Classloader B: "; showClassLoaderHierarchy(classLoaderB); println ""; println "Now loading Load classes A and B from seperate classloaders:"; def loadA = classLoaderA.loadClass("LoadA").newInstance(); def loadB = classLoaderB.loadClass("LoadB").newInstance(); print "LoadA: heirachry"; showObjectClassLoaderHierarchy(loadA); print "LoadB: heirachry"; showObjectClassLoaderHierarchy(loadB); println ""; println "Now pulling the data objects from both and comparing classloders and static data: "; def dobjA = loadA.getDataObj(); def dobjB = loadB.getDataObj(); println "dataA static field:" + dobjA.getData(); println "dataA static field hashcode: " + dobjA.getData().hashCode(); println "dataA hashcode: " + dobjA.hashCode(); println "dataA classloader: "; showObjectClassLoaderHierarchy(dobjA); println "dataB static field: " + dobjB.getData(); println "dataB static field hashcode: " + dobjB.getData().hashCode(); println "dataB hashcode: " + dobjB.hashCode(); println "dataB classLoader:"; showObjectClassLoaderHierarchy(dobjB);
The results are:
Setting up child classLoaders A and B... Classloader A heirachry: Classloader: 1926764753 Child of: 1163157884 Child of: 1022308509 Classloader B: Classloader: 846238611 Child of: 1163157884 Child of: 1022308509 Now loading Load classes A and B from seperate classloaders: LoadA: heirachryClassloader: 1926764753 Child of: 1163157884 Child of: 1022308509 LoadB: heirachryClassloader: 846238611 Child of: 1163157884 Child of: 1022308509 Now pulling the data objects from both and comparing classloders and static data: dataA static field:Loaded By B dataA static field hashcode: 1828548084 dataA hashcode: 2083117811 dataA classloader: Classloader: 1163157884 Child of: 1022308509 dataB static field: Loaded By B dataB static field hashcode: 1828548084 dataB hashcode: 157683534 dataB classLoader: Classloader: 1163157884 Child of: 1022308509
You see that LoadA and LoadB both have different classloaders, but they share a parent classloader.
The parent classloader loads the DataObj for both instances of the LoadA.dataObj and LoadB.dataObj.
LoadA.dataObj and LoadB.dataObj have different hashcodes.
However, LoadA.dataObj.data and LoadB.dataObj.data have the same hashcode, because this is the static object. They also have the same value. LoadB instantiates it’s dataObj last and sets the string to “Loaded By B”