A few days ago, a friend was asked about class loading during an interview. He answered questions related to class loading in a regular way, and he could handle it as long as he memorized the interview questions a little.
But if you ask deeper, just a little deeper, you can cool a large number of people.
For example: Why does the tomcat class loader violate the parental delegation model?
In order to prevent you from encountering this kind of situation during the interview, this article will arrange this matter.
We divide it into 4 parts to discuss:
I think, before studying tomcat class loading, we should review or consolidate the java default class loader. When I first started learning JVM, I was confused about the class loading mechanism, so I would like to take this opportunity to share it with you.
The conversion of the code compilation result from local machine code to bytecode is a small step in the storage format, but a big step in the development of programming languages.
The Java virtual machine loads the data describing the class from the Class file into the memory, verifies the data, converts, parses and initializes it, and finally forms a Java type that can be used directly by the virtual machine. This is the function of the virtual machine. Class loading mechanism.
The virtual machine design team implemented the action of "obtaining a binary byte stream describing this class through the fully qualified name of a class" in the class loading phase outside the Java virtual machine, so that the application Decide for yourself how to get the classes you need. The code module that implements this action is called a "class loader".
Although the class loader is only used to implement the loading action of the class, its role in the Java program is far from limited to the class loading stage. For any class, the class loader that loads it and the class itself need to establish its uniqueness in the Java virtual machine. Each class loader has an independent class namespace.
This sentence can be expressed in a more popular way: comparing whether two classes are "equal" only makes sense if the two classes are loaded by the same class loader. Otherwise, even if this Two classes come from the same Class file and are loaded by the same virtual machine. As long as the class loaders that load them are different, the two classes must not be equal.
1. From the perspective of the Java virtual machine, there are only two different class loaders: one is the boot class loader (Bootstrap ClassLoader) , this class loader is implemented in C language (HotSpot only) and is part of the virtual machine itself; the other is all other class loaders, which are implemented in Java language and are independent of the virtual machine, and All inherit from the abstract class java.lang.ClassLoader
.
2. From the perspective of Java developers, class loading can be divided into more details. Most Java programmers The following three system-provided class loaders will be used:
sun.misc.Launcher$ExtClassLoader
, which is responsible for All class libraries in the JAVA_HOME/lib/ext directory, or in the path specified by the java.ext.dirs system variable. Developers can use the extension class loader directly. sun.misc.Launcher$AppClassLoader
. Since this class loader is the return value of the getSystemClassLoader method in ClassLoader, it also becomes the system class loader. It is responsible for loading the class library specified on the user class path (ClassPath). Developers can use this class loader directly. If the application does not define its own class loader, this is usually the default class loader in the program. The relationship between these class loaders is generally as shown in the figure below:
In the picture The relationship between each class loader is called Parent Delegation Model (Parents Dlegation Mode) of the class loader. The parent delegation model requires that in addition to the top-level startup class loader, all other class loaders should be loaded by their own parent class loaders. The parent-child relationship between class loaders here is generally not realized by inheritance, but by inheritance. They all use composition relationships to reuse the code of the parent loader.
Note: During the interview, the interviewer may also ask which directory the corresponding class loader loads.
The parent delegation model of the class loader was introduced during JDK1.2 and is widely used in all subsequent Java programs, but it is not a mandatory constraint model, but a Java design A class loader implementation method recommended to developers.
The working process of the parent delegation model is: if a class loader receives a class loading request, it will not try to load the class itself first, but delegates the request to the parent class loader to complete. This is true for every level of class loader, so all loading requests should eventually be sent to the top-level startup class loader. Only when the parent loader feedbacks that it cannot complete the request (the required one is not found in its search scope) class), the subloader will try to load it by itself.
If the parent delegation model is not used and each class loader loads it by itself, if the user writes a class called java.lang.Object and puts it in the ClassPath of the program, the system will When multiple different Object classes appear, the most basic behavior in the Java type system cannot be guaranteed. Applications will also become a mess.
Very simple: all the code is in the loadClass method in java.lang.ClassLoader
, the code is as follows:
The logic is clear and easy to understand: first check whether it has been loaded. If not, call the loadClass method of the parent loader. If the parent loader is empty, the startup class loader will be used as the parent by default. Loader. If the parent class fails to load, throw a ClassNotFoundException exception and then call its own findClass method to load it.
We just said that the parent delegation model is not a mandatory constraint model, but a suggested class loader implementation. Most class loaders in the Java world follow this model, but there are exceptions. So far, the parental delegation model has been "broken" on a large scale three times.
The first time: before the emergence of the parental delegation model-----that is, before the release of JDK1.2.
The second time: It was caused by the flaws of the model itself. We say that the parent delegation model well solves the problem of unifying the base classes of each class loader (the more basic classes are loaded by the upper-level loader). The reason why the base class is called "basic" is Because they are always used as APIs called by user code, but there is no guarantee. What if the base class calls user code?
This is not impossible. A typical example is the JNDI service. JNDI is now a standard service in Java. Its code is loaded by the startup class loader (rt.jar that was put in JDK1.3), but it needs to be called and implemented by an independent vendor. And deploy the JNDI interface provider (SPI, Service Provider Interface) code under the ClassPath of the application, but it is impossible for the startup class loader to "know" these codes. Because these classes are not in rt.jar
, but they need to be loaded to start the class loader. How to do it?
In order to solve this problem, the Java design team had to introduce a less elegant design: Thread Context ClassLoader. This class loader can be set through the setContextClassLoader method of the java.lang.Thread
class. If it has not been set when the thread is created, it will inherit one from the parent thread. If not set too much in the global scope of the application, then this class loader will default to the application class loader.
Hey, with the thread context loader, the JNDI service uses this thread context loader to load the required SPI code, that is, the parent class loader requests the child class loader to complete the class loading action. This This behavior actually opens up the hierarchy of the parent delegation model to reversely use the class loader, which actually violates the general principles of the parent delegation model. But there is nothing you can do about it. All loading actions involving SPI in Java basically use this method. For example JNDI, JDBC, JCE, JAXB, JBI, etc.
The third time: In order to achieve hot-swap, hot-deployment, and modularization, which means adding a function or subtracting a function without restarting, you only need to replace the module together with the class loader. This achieves hot replacement of code.
The book also says:
There is basically a consensus in Java programs: OSGI’s use of class loaders is worth learning. Once you understand the implementation of OSGI, you can It can be regarded as mastering the essence of class loader.
Awesome! ! !
Now, we have basically understood the working principle of Java default class loading and also know the parent delegation model. Having said so much, I almost forgot about our tomcat. Our topic is why does the Tomcat loader violate the parental delegation model?
Let’s talk about our tomcat class loader.
First of all, let’s ask a question:
Is it okay if Tomcat uses the default class loading mechanism?
Let’s think about it: Tomcat is a web container, so what problems does it need to solve:
Let’s look at our question again: Is it okay if Tomcat uses the default class loading mechanism?
The answer is no. Why? Let’s look at the first problem. If you use the default class loader mechanism, you cannot load two different versions of the same class library. The default accumulator only cares about your fully qualified class no matter what version you have. name, and only one copy.
The second question is that the default class loader can be implemented because its responsibility is to ensure uniqueness. The third question is the same as the first question. Let's look at the fourth question again. We think how do we implement hot modification of jsp files (the name given by the poster). The jsp file is actually a class file. So if it is modified, but the class name is still the same, the class loader will directly If the jsp already exists in the method area is retrieved, the modified jsp will not be reloaded.
So what should we do? We can directly uninstall the class loader of this jsp file, so you should have thought that each jsp file corresponds to a unique class loader. When a jsp file is modified, the jsp class loader is directly unloaded. Re-create the class loader and reload the jsp file.
So, how does Tomcat implement it? The awesome Tomcat team has already designed it. Let’s take a look at their design drawings:
We see that the first three class loadings are consistent with the default ones. CommonClassLoader, CatalinaClassLoader, SharedClassLoader and WebappClassLoader are Tomcat’s own class loaders. They load /common/*
, /server/*
, /shared/*
respectively (after tomcat 6, they have been merged into the lib directory in the root directory ) and the Java class library in /WebApp/WEB-INF/*
. There are usually multiple instances of the WebApp class loader and the Jsp class loader. Each Web application corresponds to a WebApp class loader, and each JSP file corresponds to a Jsp class loader.
commonLoader
: Tomcat’s most basic class loader. The classes in the loading path can be accessed by the Tomcat container itself and each Webapp; catalinaLoader
: Tomcat container private class loader, the classes in the loading path are not visible to the Webapp; sharedLoader
: Class loader shared by each Webapp. The classes in the loading path are visible to all Webapps, but not visible to the Tomcat container; WebappClassLoader
: Private to each Webapp Class loader, the classes in the loading path are only visible to the current Webapp;It can be seen from the delegation relationship in the figure:
CommonClassLoader can load all classes It can be used by Catalina ClassLoader and SharedClassLoader, thus realizing the sharing of public class libraries, while the classes that CatalinaClassLoader and Shared ClassLoader can load are isolated from each other.
WebAppClassLoader can use the classes loaded by SharedClassLoader, but each WebAppClassLoader instance is isolated from each other.
The loading scope of JasperLoader is only the .Class file compiled by this JSP file. The purpose of its appearance is to be discarded: when the Web container detects that the JSP file has been modified, it will replace the current An instance of JasperLoader, and create a new Jsp class loader to implement the HotSwap function of the JSP file.
Okay, so far, we already know why tomcat is designed this way and how it is designed. So, does tomcat violate the parent delegation model recommended by Java? The answer is: violated.
We said before:
The parent delegation model requires that in addition to the top-level startup class loader, the rest of the class loaders should be loaded by their own parent class The server is loaded.
Obviously, tomcat is not implemented in this way. In order to achieve isolation, tomcat does not comply with this agreement. Each webappClassLoader loads the class file in its own directory and will not pass it to the parent class loader.
We extend a question: What should we do if tomcat's Common ClassLoader wants to load classes in WebApp ClassLoader? After reading the previous content about destroying the parent delegation model, we have a clear idea. We can use the thread context class loader to implement it. Using the thread context loader, the parent class loader can request the child class loader to complete the class loading action. .
Isn’t it awesome?
Okay, finally, we understand why Tomcat violates the parent delegation model, and we also know how tomcat's class loader is designed. By the way, I reviewed Java's default class loader mechanism, and also learned how to destroy Java's class loading mechanism. This time the harvest is not small! ! !
The above is the detailed content of Interviewer: Why does the tomcat class loader violate the parental delegation model?. For more information, please follow other related articles on the PHP Chinese website!