GitHub 19K Star Java engineer into god’s path, not to learn about it!

GitHub 19K Star Java engineer into god’s road, really not to learn about it!

GitHub 19K Star Java engineer into god’s road, really really not to learn about it!

These days, I love to ask my parents’ assignment questions during the interview process, because I find that this question really helps me understand the full range of a candidate.

In an interview the other day, I was talking to a candidate about the class loading mechanism of the JVM. He talked about parental delegation and confidently told me what he understood about parental delegation.

It was rare to meet a candidate who knew much about the block, so we had a “300 round “exchange, and by the time we had asked these questions, about half an hour had passed.

Finally, this follow-up told me, “I never expected that I, a technical manager for 7 years, would be abused by my parents!!”

Let’s review the questions I asked him and see how many you can answer:

1. What is parental delegation?

2. Why do parents need to delegate? What’s wrong with not delegating?

3. Is the relationship between parent and child loaders inherited?

4. How is parental delegation implemented?

5. Can I actively break this parental delegation mechanism? How?

How can overwriting a loadClass method break a parent delegate? What is the difference between findClass () and defineClass ()?

7. Name a time you know when parental delegation has been broken

8. Why do JNDI, JDBC, etc need to break parent delegates?

9. Why did TOMCAT break the parent delegate?

Talk about your understanding of modular technology!

Above, 10 questions, start from the beginning to answer, you can probably adhere to the number of questions?

What is the parent delegate mechanism

First of all, we know that the virtual machine needs to use a class loader to load a class. In Java, there are many class loaders. So when the JVM wants to load a. Class file, which class loader should it load?

This brings us to the “parent delegation mechanism.”

First, we need to know that the Java language system supports the following four types of loaders:

  • Bootstrap ClassLoader starts the ClassLoader
  • Extention ClassLoader standard extension ClassLoader
  • Application ClassLoader Indicates the Application ClassLoader
  • User ClassLoader user-defined ClassLoader

There is a hierarchical relationship among these four loaders, as shown in the figure below

The previous loader is generally considered to be the parent of the next loader, so all loaders except BootstrapClassLoader have a parent loader.

When a class loader receives a request for a class load, it does not load the specified class directly. Instead, it delegates the request to its parent. The current loader is responsible for loading the class only if the parent loader is unable to load the class.

So, when does a parent loader fail to load a class?

The four types of loaders provided in Java have their own responsibilities:

  • The Bootstrap ClassLoader is responsible for loading the Java core libraries, such as rt.jar, resources. Jar, charsets. Jar, and class in %JRE_HOME%\lib.
  • Extention ClassLoader is responsible for loading JAR packages and class files in directory %JRE_HOME%\lib\ext.
  • Application ClassLoader is responsible for loading all classes in the current Application’s classpath
  • User ClassLoader: user-defined class loader that loads class files in a specified path

So, a user-defined class such as com.Hollis.clAsshollis will never be loaded by Bootstrap and Extention loaders anyway.

Why parent delegate?

As we mentioned above, because class loaders are strictly hierarchical, Java classes are hierarchical as well.

Or the hierarchy is priority.

For example, a class defined in the java.lang package, because it is stored in rt.jar, will be summarized during the loading process, and will be delegated to the Bootstrap ClassLoader, which will eventually load it.

A user-defined com.Hollis. ClassHollis class can also be delegated to the Bootstrap ClassLoader, but since Bootstrap ClassLoader is not responsible for loading this class, The Extention ClassLoader is not responsible for loading the class before it is loaded by the Application ClassLoader.

This mechanism has several benefits.

First, you can avoid reloading classes by delegating. When the parent loader has already loaded a class, the child loader will not reload the class.

In addition, security is ensured through parental delegation. Since Bootstrap ClassLoader only loads classes in jar packages in JAVA_HOME, such as java.lang.Integer, this class will not be replaced unless someone runs into your machine and destroys your JDK.

Then, someone can avoid having a custom java.lang.Integer loaded with destructive functionality. This effectively prevents the core Java API from being tampered with.

Is the relationship between parent and child loaders inherited?

Many people see names like parent loader and child loader and assume that class loaders in Java inherit from one another.

There are even many articles on the Internet with similar erroneous views.

To be clear, in the parent delegate model, the parent-child relationship between class loaders is generally not implemented by Inheritance, but by Composition.

The following is the definition of a parent loader in ClassLoader:

public abstract class ClassLoader {
    // The parent class loader for delegation
    private final ClassLoader parent;
}
Copy the code

How is parental delegation implemented?

The parental delegation model is important to ensure the stable operation of Java programs, but its implementation is not complicated.

The code to implement parental delegation is concentrated in the loadClass() method of java.lang.classLoader:

protected Class<? > loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<? > c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent ! = null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }}Copy the code

The code is not difficult to understand, mainly the following steps:

Check whether the class is already loaded. 2. If the class is not loaded, call the loadClass() method of the parent loader to load it. 4. If the parent class fails to load, throw a ClassNotFoundException and call your own findClass() method to load it.

How do you actively break parental delegation?

Knowing the implementation of the parent delegate model, it’s easy to break the parent delegate mechanism.

Since all of his parent delegate procedures are implemented in the loadClass method, to break this mechanism, you can define a class loader and override the loadClass method so that it does not delegate.

LoadClass (), findClass (), defineClass ()

There are a lot of ClassLoader methods related to class loading, such as loadClass, findClass and defineClass.

  • loadClass()
    • Is a method that mainly does class loading, and the default parent delegate mechanism is implemented in this method.
  • findClass()
    • Loads. Class bytecode by name or location
  • definclass()
    • Convert bytecode to Class

Here we need to expand on loadClass and findClass. As we said earlier, when we want to customize a classloader, like breaking the parent delegate principle, we override the loadClass method.

So what if we want to define a classloader, but don’t want to break the parent delegate model?

At this point, you can inherit the ClassLoader and override the findClass method. The findClass() method is a new addition to ClassLoader since JDK1.2.

/** * @since 1.2 */ protected Class<? > findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }Copy the code

This method only throws an exception and has no default implementation.

After JDK1.2, users are no longer encouraged to override the loadClass() method directly, instead implementing their own classloading logic into the findClass() method.

In the logic of the loadClass() method, if the parent class loader fails to load, it calls its own findClass() method to complete the load.

So, if you want to define your own ClassLoader that adheres to the parent delegate model, you can inherit ClassLoader and implement your own loading logic in findClass.

Example of a broken parent delegate

Breaking parental delegation is nothing new, and many frameworks, containers, and so on break this mechanism to achieve certain functions.

The first situation that is broken is before parental delegation occurs.

Since the parent delegate model was introduced after JDK1.2, custom class loaders were already in use. So, these are not following the parental delegation principle.

The second is when JNDI, JDBC, etc., need to load SPI interface implementation classes.

** The third is to implement hot plug hot deployment tools. ** In order for the code to work dynamically without rebooting, hot replacement is achieved by replacing the module with the same loader.

The fourth is the emergence of Web containers such as Tomcat.

The fifth is the application of modular technology such as OSGI and Jigsaw.

Why do JNDI, JDBC, etc need to break parent delegates?

In our daily development, most of the basic classes provided by Java are called through API, and these basic classes are loaded by Bootstrap.

However, in addition to API, there is also an SPI method of calling.

As with a typical JDBC service, we typically create a database connection by:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql", "root", "1234");
Copy the code

Before the above code is executed, DriverManager is loaded by the class loader, because the java.sql.DriverManager class is underneath rt.jar, so it is loaded by the root loader.

When a class is loaded, static methods of the class are executed. One key piece of code is:

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Copy the code

This code will attempt to load any implementation classes under the classpath that implement the Driver interface.

So, here’s the problem.

DriverManager is loaded by the root loader, so when the above code is encountered during loading, it will try to load all the Driver implementation classes, but these implementation classes are provided by a third party. According to the parent delegate principle, the third party classes cannot be loaded by the root loader.

So, how to solve this problem?

Thus, the parent delegate principle is broken in JDBC by introducing ThreadContextClassLoader (thread context loader, by default AppClassLoader).

If we dig into the Serviceloader.load method, we can see:

public static <S> ServiceLoader<S> load(Class<S> service) {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}
Copy the code

The first line gets the current thread’s thread-context classloader, AppClassLoader, used to load the concrete implementation classes in the classpath.

Why did Tomcat break the parent delegate

As we know, Tomcat is a Web container, so a Web container may require multiple applications to be deployed.

Different applications may rely on different versions of the same third-party library, but the full pathname of a class in the library may be the same.

For example, multiple applications rely on Hollis. jar, but application A needs to rely on version 1.0.0, while application B needs to rely on version 1.0.1. One of the classes in both versions is com.Hollis.test.class.

If you use the default parent delegate class loading mechanism, you cannot load multiple identical classes.

Therefore, Tomcat breaks the parent delegate principle and provides a separate WebAppClassLoader for each Web container by providing a mechanism for isolation.

Tomcat class loading mechanism: In order to achieve isolation, classes defined by the Web application are loaded first. Therefore, the convention of parental delegation is not followed. Each application’s own class loader — WebAppClassLoader is responsible for loading the class files in its own directory. This is the opposite of parental delegation.

Modularity technology and class loading mechanism

Modularity technology has matured in recent years, and modularity has been used in JDK 9.

The OSGI framework was modular long before JDK 9, and OSGI’s ability to hot-plug modules and accurately control visibility within modules is due to its special class loading mechanism. The relationship between loaders is no longer a tree structure of parental delegation model, but a complex network structure.

Parental delegation is not absolute in the JDK either.

Prior to JDK9, the base classes of the JVM used to be in the rt.jar package, which is the bedrock of the JRE.

Not only is this a violation of the single responsibility principle, but the program will also be compiled to pack up a lot of useless classes, resulting in bloat.

In JDK9, the entire JDK is built based on modularity. The former Rt.jar and tool.jar are divided into dozens of modules.

Class<? > c = findLoadedClass(cn); If (c == null) {LoadedModule LoadedModule = findLoadedModule(cn); if (loadedModule ! BuiltinClassLoader loader = loadedModule.loader(); C = findClassInModuleOrNull(loadedModule, cn); } else {// Parent delegate if (parent! = null) { c = parent.loadClassOrNull(cn); }}}Copy the code

conclusion

Above, from what is the parent delegate, to how to implement and destroy the parent delegate, and from the example of breaking the parent delegate and so on many aspects of a comprehensive introduction about the knowledge of parent delegate.

You will have a better understanding of parental delegation through this article.

After reading this article, write on your resume: Familiar with Java class loading mechanism.

About the author: Hollis, a person with a unique pursuit of Coding, is a technical expert of Alibaba, co-author of “Three Courses for Programmers”, and author of a series of articles “Java Engineers becoming Gods”.

If you have any comments, suggestions, or want to communicate with the author, you can follow the public account [Hollis] and directly leave a message to me in the background.