302

What is the difference between a thread's context class loader and a normal class loader?

That is, if Thread.currentThread().getContextClassLoader() and getClass().getClassLoader() return different class loader objects, which one will be used?

Hearen
  • 7,420
  • 4
  • 53
  • 63
abracadabra
  • 3,067
  • 3
  • 17
  • 7

4 Answers4

196

This does not answer the original question, but as the question is highly ranked and linked for any ContextClassLoader query, I think it is important to answer the related question of when the context class loader should be used. Short answer: never use the context class loader! But set it to getClass().getClassLoader() when you have to call a method that is missing a ClassLoader parameter.

When code from one class asks to load another class, the correct class loader to use is the same class loader as the caller class (i.e., getClass().getClassLoader()). This is the way things work 99.9% of the time because this is what the JVM does itself the first time you construct an instance of a new class, invoke a static method, or access a static field.

When you want to create a class using reflection (such as when deserializing or loading a configurable named class), the library that does the reflection should always ask the application which class loader to use, by receiving the ClassLoader as a parameter from the application. The application (which knows all the classes that need constructing) should pass it getClass().getClassLoader().

Any other way to obtain a class loader is incorrect. If a library uses hacks such as Thread.getContextClassLoader(), sun.misc.VM.latestUserDefinedLoader(), or sun.reflect.Reflection.getCallerClass() it is a bug caused by a deficiency in the API. Basically, Thread.getContextClassLoader() exists only because whoever designed the ObjectInputStream API forgot to accept the ClassLoader as a parameter, and this mistake has haunted the Java community to this day.

That said, many many JDK classes use one of a few hacks to guess some class loader to use. Some use the ContextClassLoader (which fails when you run different apps on a shared thread pool, or when you leave the ContextClassLoader null), some walk the stack (which fails when the direct caller of the class is itself a library), some use the system class loader (which is fine, as long as it is documented to only use classes in the CLASSPATH) or bootstrap class loader, and some use an unpredictable combination of the above techniques (which only makes things more confusing). This has resulted in much weeping and gnashing of teeth.

When using such an API, first, try to find an overload of the method that accepts the class loader as a parameter. If there is no sensible method, then try setting the ContextClassLoader before the API call (and resetting it afterwards):

ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    // call some API that uses reflection without taking ClassLoader param
} finally {
    Thread.currentThread().setContextClassLoader(originalClassLoader);
}
yonran
  • 18,156
  • 8
  • 72
  • 97
  • 10
    Yes, this is the answer I'd point out to anyone asking the question. – Marko Topolnik Dec 22 '16 at 13:00
  • 13
    This answer focuses on using the class loader for loading classes (to instantiate them through reflection or similar), while another purpose it's used for (and, in fact, the only purpose I've ever used it for personally) is loading resources. Does the same principle apply, or are there situations where you'd want to fetch resources through the context class loader rather than the caller class loader? – Egor Hans Nov 18 '19 at 20:44
  • 3
    Beware that `getClass().getClassLoader()` is not necessarily the same as `ThisClass.class.getClassLoader()`, unless `ThisClass` is `final` or you otherwise know that subclasses do not exist. – Jesse Glick Jul 06 '21 at 13:47
  • Suppose if i set original class loader in child thread does it gives some problems. ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(originalClassLoader ); // call some API that uses reflection without taking ClassLoader param } – Shadab Jul 18 '22 at 07:57
173

Each class will use its own classloader to load other classes. So if ClassA.class references ClassB.class then ClassB needs to be on the classpath of the classloader of ClassA, or its parents.

The thread context classloader is the current classloader for the current thread. An object can be created from a class in ClassLoaderC and then passed to a thread owned by ClassLoaderD. In this case the object needs to use Thread.currentThread().getContextClassLoader() directly if it wants to load resources that are not available on its own classloader.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
David Roussel
  • 5,788
  • 1
  • 30
  • 35
  • 1
    Why do you say that `ClassB` must be on the classpath of `ClassA`'s loader (or `ClassA`'s loader's parents)? Isn't it possible for `ClassA`'s loader to override [`loadClass()`](http://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String,%20boolean)), such that it can successfully load `ClassB` even when `ClassB` is not on its classpath? – Pacerier Aug 25 '14 at 04:49
  • 11
    Indeed, not all classloaders have a classpath. When wrote "ClassB needs to be on the classpath of the classloader of ClassA", I meant "ClassB needs to loadable by the classloader of ClassA". 90% of the time they mean the same. But if you are not using a URL based classloader, then only the second case is true. – David Roussel Aug 26 '14 at 11:19
  • What does it mean to say that "`ClassA.class` references `ClassB.class`"? – jameshfisher Aug 26 '14 at 14:25
  • 2
    When ClassA has an import statement for ClassB, or if there is a method in ClassA that has a local variable of type ClassB. That will trigger ClassB to be loaded, if it's not loaded already. – David Roussel Aug 27 '14 at 09:46
  • I think my problem is connected with this topic. What do you think about my sollution? I know that it's not a good pattern, but I don't have any other idea how to fix it: http://stackoverflow.com/questions/29238493/org-jboss-mx-loading-loadmgr3-beginloadtask-after-invoke-clustered-jms-apps – Marcin Erbel Mar 25 '15 at 16:42
97

There is an article on infoworld.com that explains the difference => Which ClassLoader should you use

(1)

Thread context classloaders provide a back door around the classloading delegation scheme.

Take JNDI for instance: its guts are implemented by bootstrap classes in rt.jar (starting with J2SE 1.3), but these core JNDI classes may load JNDI providers implemented by independent vendors and potentially deployed in the application's -classpath. This scenario calls for a parent classloader (the primordial one in this case) to load a class visible to one of its child classloaders (the system one, for example). Normal J2SE delegation does not work, and the workaround is to make the core JNDI classes use thread context loaders, thus effectively "tunneling" through the classloader hierarchy in the direction opposite to the proper delegation.

(2) from the same source:

This confusion will probably stay with Java for some time. Take any J2SE API with dynamic resource loading of any kind and try to guess which loading strategy it uses. Here is a sampling:

  • JNDI uses context classloaders
  • Class.getResource() and Class.forName() use the current classloader
  • JAXP uses context classloaders (as of J2SE 1.4)
  • java.util.ResourceBundle uses the caller's current classloader
  • URL protocol handlers specified via java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only
  • Java Serialization API uses the caller's current classloader by default
Vampire
  • 35,631
  • 4
  • 76
  • 102
Timour
  • 2,081
  • 20
  • 24
  • As it is suggested that the workaround is to make the core JNDI classes use thread context loaders, I didn't understand how this helps in this case.We want to load the implementation vendor classes using parent classloader but they are not visible to parent classloader. So how can we load them using parent, even if we set this parent classloader in context classloader of thread. – Sunny Gupta Aug 07 '13 at 16:58
  • 6
    @SAM, the suggested workaround is actually quite the opposite from what you're saying at the end. It's not the parent `bootstrap` class loader being set as the context class loader but the child `system` classpath class loader that the `Thread` is being set up with. The `JNDI` classes are then making sure to use `Thread.currentThread().getContextClassLoader()` to load the JNDI implementation classes available on the classpath. – Ravi K Thapliyal Sep 05 '13 at 19:10
  • "Normal J2SE delegation does not work", may I know why it does not work? Because the Bootstrap ClassLoader can only load class from rt.jar and cannot load the class from the application's -classpath? Right? – YuFeng Shen Oct 02 '18 at 16:45
44

Adding to @David Roussel answer, classes may be loaded by multiple class loaders.

Lets understand how class loader works.

Bootstrap ClassLoader is responsible for loading standard JDK class files from rt.jar and it is parent of all class loaders in Java. Bootstrap class loader don't have any parents.

Extension ClassLoader delegates class loading request to its parent, Bootstrap and if unsuccessful, loads class form jre/lib/ext directory or any other directory pointed by java.ext.dirs system property

System or Application class loader and it is responsible for loading application specific classes from CLASSPATH environment variable, -classpath or -cp command line option, Class-Path attribute of Manifest file inside JAR.

Application class loader is a child of Extension ClassLoader and its implemented by sun.misc.Launcher$AppClassLoader class.

ClassLoader follows three principles.

Delegation principle

A class is loaded in Java, when its needed. Suppose you have an application specific class called Abc.class, first request of loading this class will come to Application ClassLoader which will delegate to its parent Extension ClassLoader which further delegates to Primordial or Bootstrap class loader

NOTE: Except Bootstrap class loader, which is implemented in native language mostly in C, all Java class loaders are implemented using java.lang.ClassLoader.

Visibility Principle

According to visibility principle, Child ClassLoader can see class loaded by Parent ClassLoader but vice-versa is not true.

Uniqueness Principle

According to this principle a class loaded by Parent should not be loaded by Child ClassLoader again

Source :javin paul blog in javarevisited :

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211