Java Classloader mechanism usage code parsing

  • 2020-12-16 05:57:04
  • OfStack

To develop Java, the mechanism of ClassLoader is a basic knowledge that must be familiar with. This paper makes a brief summary of the mechanism of Java ClassLoader. Because different implementations of JVM differ, the content described in this article is limited to Hotspot Jvm.

This paper will discuss and summarize ClassLoader provided by JDK by default, the two-parent delegate model, how to customize ClassLoader and the scenario of breaking the two-parent delegate mechanism in Java.

The default ClassLoader JDK

JDK provides the following ClassLoader by default

Bootstrp loader

The Bootstrp loader, written in the C++ language, is initialized after the Java virtual machine starts up and is mainly responsible for loading the path specified by the %JAVA_HOME%/jre/lib, -Xbootclasspath parameter and the classes in %JAVA_HOME%/jre/classes.

ExtClassLoader

Bootstrp loader loads ExtClassLoader and sets the parent loader of ExtClassLoader to Bootstrp ES52en. ExtClassLoader is written with Java, specifically, sun. misc. Launcher$ExtClassLoader, ExtClassLoader mainly loads %JAVA_HOME%/jre/ es663en /ext, All classes directories under this path and the class libraries in the path specified by the java.ext.dirs system variable.

AppClassLoader

After Bootstrp loader has loaded ExtClassLoader, AppClassLoader is loaded and the parent loader of AppClassLoader is specified as ExtClassLoader. AppClassLoader is also written in Java, and its implementation class is ES83en.misc.Launcher $AppClassLoader. In addition, we know that there is an getSystemClassLoader method in ClassLoader, which returns AppclassLoader.AppClassLoader is mainly responsible for loading the class at the location specified by classpath or the jar document, which is also the default classloader for Java program.

Two-parent delegation model

In Java, the loading of ClassLoader adopts the parent delegate mechanism. When the parent delegate mechanism is used to load the class, the following steps are adopted:

The current ClassLoader first checks whether the class has been loaded from its own loaded class, and if so, returns the originally loaded class.

Each class loader has its own load cache. When a class is loaded, it will be put into the cache and returned the next time it is loaded.

When no loaded class is found in the current classLoader cache, the parent class loader is delegated to load. The parent class loader adopts the same strategy, first checking its own cache, then delegates to the parent class to load, 1 until bootstrp ClassLoader.

When none of the parent classloaders is loaded, it is loaded by the current classloader and placed in its own cache to return the next time a load request is made.

At this point you might be wondering, why does Java have such a delegation mechanism? To understand this, let's introduce another concept about Classloader, "namespace", which means to identify a class that requires the fully qualified name of the class and the ClassLoader to load it. That is, even if the fully qualified names of the two classes are the same, they are different classes in JVM because different ClassLoader loaded the class. Now that we understand namespaces, let's look at the delegate model. Delegation model is adopted after increasing the different ClassLoader interaction ability, such as said above, we JDK Bunsen provided by libraries, such as hashmap linkedlist, etc., these classes by bootstrp class loader to load, no matter you have how many class loader in the program, all of these classes are then can be Shared, thus avoiding the different class loaders loaded the chaos after of the same name is not the same.

How do I customize ClassLoader

Java allows applications to customize classloader in addition to the default classloader mentioned above, so to customize classloader we need to inherit java.lang.ClassLoader. Let's take a look at some important ways to customize Classloader:

1. loadClass method

loadClass method declare


public Class<?> loadClass(String name) throws ClassNotFoundException

Above is the prototype declaration for the loadClass method, where the aforementioned implementation of the parent delegate mechanism is actually implemented. Let's take a look at the code for this method to see how it actually implements the parent delegate.

loadClass method implement


public Class<?> loadClass(String name) throws ClassNotFoundException
 { 
return loadClass(name, false);
}

As you can see above, the loadClass method calls the loadcClass(name,false) method, so let's look at the implementation of another loadClass method.

Class loadClass(String name, boolean resolve)


protected synchronized Class<?> loadClass(String name, Boolean resolve) throws ClassNotFoundException  
 {
	// First, check if the class has already been loaded 
	Class c = findLoadedClass(name);
	// check class Has it been loaded  
	if (c == null)
	{
		try {
			if (parent != null) {
				c = parent.loadClass(name, false);
				// If it is not loaded, and the parent loader is specified, the parent loader is delegated to load. 
			} else {
				c = findBootstrapClass0(name);
				// If there is no parent class loader, delegate bootstrap Loader loading    }
			}
			catch (ClassNotFoundException e) {
				// If still not found, then invoke findClass in order     
				// to find the class.     
				c = findClass(name);
				// If the parent class load is not loaded to, it passes its own findClass To load.    }
			}
			if (resolve) 
			{
				resolveClass(c);
			}
			return c;
		}

The code above, I've annotated it so you can see clearly how loadClass's parental delegation mechanism works. One thing to notice here is public Class < ? > loadClass(String name) throws ClassNotFoundException is not marked as final, which means that we can override, which means that the parental delegation mechanism can be broken. In addition, notice that there is an findClass method, so let's talk about how it works.

2.findClass

We looked at the source code of ES185en. lang. ClassLoader and found the implementation of findClass as follows:


 protected Class<?> findClass(String name) throws ClassNotFoundException
 { 
throw new ClassNotFoundException(name);
}

We can see that the default implementation of this method is to throw an exception directly, but this method is actually left to our application to override. Depending on your implementation logic, you can either read from disk or get the byte stream of the class file from the network. Once you get the class2 base, you can pass it on to defineClass to do the next step of loading. We will describe defineClass below. ok, based on the above analysis, we can draw the following conclusions:

When we were writing our OWN ClassLoader, we only needed override findClass if we wanted to follow the parent delegate mechanism.

3.defineClass

Let's look at the defineClass source code first:

defineClass


protected final Class<?> defineClass(String name, byte[] b, int off, int len) 
throws ClassFormatError
{   
 return defineClass(name, b, off, len, null);
}

From the above code we can see that this method is defined as final, which means that this method cannot be used by Override. This is actually the only entry left by jvm, through which jvm guarantees that the class file must conform to the class definition specified by the Java virtual machine specification. This method ends up calling native's method to do the actual class loading.

Ok, from the above description, let's consider the following question:

If we write a class of ES226en.lang.String ourselves, can we replace the class of JDK itself?

The answer is no. We can't do that. Why is that? I've seen a lot of explanations on the Internet that say that the parental delegation mechanism solves this problem, but it's actually not very accurate. Since the parent delegate mechanism can be broken, you could write your own classLoader to load your own java.lang.String class, but you will find that it will not load successfully either, specifically because the implementation of jvm guarantees that it must be loaded by bootstrp for classes starting with java.*.

Scenarios that do not follow the "parent delegate mechanism"

As mentioned above, the parent delegate mechanism is mainly to realize the interaction between classes loaded by different ClassLoader. Classes that are common are handed over to the parent loader to be loaded. However, there are indeed cases in Java where classes loaded by the parent loader need to be loaded by the child loader. Here's how it happens.

Java in one SPI (ServiceProviderInterface) standards, using the SPI library, such as JDBC JNDI etc., as we all know JDBC need third party to provide the drive to, and driven jar package is in our application itself classpath, and jdbc itself is api jdk 1 part, it has been bootstrp loaded, the third party vendors to provide the implementation class how to load? In this context, JAVA introduces the concept of thread context classloading. The thread classloader inherits from the parent thread by default. If not specified, the default is the system classloader (AppClassLoader), so that when the third party driver is loaded, it can be loaded through the thread context classloader.

In addition, OSGI and some Javaappserver also broke the parent delegate mechanism in order to achieve more flexible classloaders.

conclusion

That is the end of this article on Java Classloader mechanism usage code parsing, I hope to help you. Interested friends can continue to refer to other related topics in this site, if there is any deficiency, welcome to comment out. Thank you for your support!


Related articles: