Please enable Javascript to view the contents

Java 内置 Class Loader

 ·  ☕ 3 分钟

内置 Classloader

The class loader delegation model

Class loaders load classes and resources present on their respective classpath:

  • System or application class loaders load classes from the application classpath
  • Extension class loaders search on the Extension classpath (JRE/lib/ext)
  • Bootstrap class loader looks on the Bootstrap classpath (JRE/lib/rt.jar)

We can customize the default class loading behavior as well. We can explicitly specify the class loader while loading a class dynamically.

However, we should note that if we load the same class from different types of class loaders, these will be seen as different resources by the JVM.

Bootstrap Class Loader

Java classes are loaded by an instance of java.lang.ClassLoader. However, class loaders are classes themselves. Hence, the question is, who loads the java.lang.ClassLoader itself*?*

This is where the bootstrap or primordial class loader comes into the picture.

It’s mainly responsible for loading JDK internal classes, typically rt.jar and other core libraries located in $JAVA_HOME/jre/lib directory. Additionally, Bootstrap class loader serves as a parent of all the other *ClassLoader* instances.

This bootstrap class loader is part of the core JVM and is written in native code as pointed out in the above example. Different platforms might have different implementations of this particular class loader.

Extension Class Loader

The extension class loader is a child of the bootstrap class loader and takes care of loading the extensions of the standard core Java classes so that it’s available to all applications running on the platform.

Extension class loader loads from the JDK extensions directory, usually $JAVA_HOME/lib/ext directory or any other directory mentioned in the java.ext.dirs system property.

System Class Loader

The system or application class loader, on the other hand, takes care of loading all the application level classes into the JVM. It loads files found in the classpath environment variable, *-classpath* or *-cp* command line option. Also, it’s a child of Extensions classloader.

Web Container

Jave EE delegation model, application class loader hierarchy

委托模式

Class loaders follow the delegation model where on request to find a class or resource, a *ClassLoader* instance will delegate the search of the class or resource to the parent class loader.

Let’s say we have a request to load an application class into the JVM. The system class loader first delegates the loading of that class to its parent extension class loader which in turn delegates it to the bootstrap class loader.

Only if the bootstrap and then the extension class loader is unsuccessful in loading the class, the system class loader tries to load the class itself.

可视性

In addition, children class loaders are visible to classes loaded by its parent class loaders.

For instance, classes loaded by the system class loader have visibility into classes loaded by the extension and Bootstrap class loaders but not vice-versa.

To illustrate this, if Class A is loaded by an application class loader and class B is loaded by the extensions class loader, then both A and B classes are visible as far as other classes loaded by Application class loader are concerned.

Class B, nonetheless, is the only class visible as far as other classes loaded by the extension class loader are concerned.

What is current classloader

在加载的 Class A 的内存元数据中,会记录加载这个 Class A 的 Classloader A。如果这个 Class A 的方法需要加载未知的其它 Class B, 默认会使用 Classloader A,这个 Classloader A 就叫 current classloader

Classloader Reference
All classes that are loaded contain a reference to the classloader that loaded them. In turn the classloader also contains a reference to all classes that it has loaded.

Java class loaders follow a hierarchical relationship.

Each request to find or load a class is delegated to the respective parent class loader. If all the ancestor class loaders are unable to find a class, then the current class loader tries to locate it. Here, “current class” implies the class of the currently executing method.

This relationship between class loaders helps in maintaining the uniqueness of resources in an application. Additionally, if a class has already been loaded by a parent class loader, the child class loader doesn’t need to reload it.

By definition, a current classloader loads and defines the class to which your current method belongs. This classloader is implied(默示) when dynamic links between classes resolve at runtime, and when you use the one-argument version of Class.forName(), Class.getResource(), and similar methods. It is also used by syntactic constructs like X.class class literals (see “Get a Load of That Name!” for more details).

What is thread context loaders

Thread context classloaders were introduced in Java 2 Platform, Standard Edition (J2SE). Every Thread has a context classloader associated with it (unless it was created by native code). It is set via the Thread.setContextClassLoader() method. If you don’t invoke this method following a Thread’s construction, the thread will inherit its context classloader from its parent Thread. If you don’t do anything at all in the entire application, all Threads will end up with the system classloader as their context classloader. It is important to understand that nowadays this is rarely the case since Web and Java 2 Platform, Enterprise Edition (J2EE) application servers utilize sophisticated classloader hierarchies for features like Java Naming and Directory Interface (JNDI), thread pooling, component hot redeployment, and so on.

In general, context class loaders provide an alternative method to the class-loading delegation scheme introduced in J2SE.

Like we learned before, classloaders in a JVM follow a hierarchical model, such that every class loader has a single parent with the exception of the bootstrap class loader.

However, sometimes when JVM core classes need to dynamically load classes or resources provided by application developers, we might encounter a problem.

For example, in JNDI, the core functionality is implemented by the bootstrap classes in rt.jar. But these JNDI classes may load JNDI providers implemented by independent vendors (deployed in the application classpath). This scenario calls for the bootstrap class loader (parent class loader) to load a class visible to the application loader (child class loader).

J2SE delegation doesn’t work here, and to get around this problem, we need to find alternative ways of class loading. This can be achieved using thread context loaders.

The java.lang.Thread class has a method, getContextClassLoader(), that returns the ContextClassLoader for the particular thread. The ContextClassLoader is provided by the creator of the thread when loading resources and classes.

If the value isn’t set, then it defaults to the class loader context of the parent thread.

Context class loaders also follow the hierarchy model. The root class loader, in this case, is the context class loader of the primordial(原始的) thread. A primordial thread is the initial thread created by the operating system.

As the application starts executing, other threads may get created. The context class loader of a primordial thread is initially set to the class loader that loads the application, i.e., the system class loader.

Suppose we don’t update the context class loader for any thread at any level of the hierarchy. As a result, we can say that by default, the context class loader for a thread is the same as the system class loader. For such scenarios, if we perform Thread.currentThread().getContextClassLoader() and getClass().getClassLoader() operations, both will return the same objects.

current classloader vs thread classloader

  • 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

Ref.

分享

Mark Zhu
作者
Mark Zhu
An old developer