Are you sure you really understand "Parental Delegation"? !

Why don’t you come to know the path to becoming a god of Java engineers at GitHub 19k Star?

Recently, during the interview process, I like to ask some questions about the appointment of my parents, because I found that this question can really help me understand a candidate in all aspects.

I remember that during an interview a few days ago, I talked to a candidate about the JVM class loading mechanism. He talked about parental delegation and confidently told me his understanding of parental delegation.

Because it is rare to encounter a candidate who knows a lot about knowledge, we launched a "300 round" confrontation. After asking these questions, about half an hour has passed.

In the end, the follow-up person said to me: " I never expected that I, a technical manager who had worked for 7 years, would be assigned to abuse by my parents!!! "

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

1. What is parental delegation? 2. Why do parents need to delegate, and what are the problems with not delegate? 3. Is the relationship between "parent loader" and "child loader" inherited? 4. How does parental delegation work? 5. Can I voluntarily destroy this parental delegation mechanism? How to destroy it? 6. Why overriding the loadClass method can break the parent delegation? What is the difference between this method and findClass() and defineClass()? 7. Tell me about the examples of parental delegation that you know about being broken. 8. Why do JNDI, JDBC, etc. need to break the parental delegation? 9. Why does TOMCAT destroy parental delegation? 10. Talk about your understanding of modular technology!

The above, 10 questions, start from the beginning, how many questions can you probably stick to?

What is the parent delegation mechanism

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

This has to mention the "parent delegation mechanism."

First of all, we need to know that the following 4 types of loaders are supported in the Java language system:

  • Bootstrap ClassLoader Bootstrap ClassLoader
  • Extention ClassLoader Standard extended class loader
  • Application ClassLoader Application ClassLoader
  • User ClassLoader User-defined class loader

There is a hierarchical relationship between these four types of loaders, as shown below

-w704

It is generally considered that the loader of the upper layer is the parent loader of the loader of the next layer. Then, except for BootstrapClassLoader, all loaders have a parent loader.

Then, the so-called parent delegation mechanism refers to: when a class loader receives a class loading request, he will not directly load the specified class, but delegate the request to its parent loader to load . Only when the parent loader cannot load the class, the current loader will be responsible for the loading of the class.

So, under what circumstances will the parent loader fail to load a certain class?

In fact, the four types of loaders provided in Java have their own responsibilities:

  • Bootstrap ClassLoader is mainly responsible for loading Java core class libraries, rt.jar, resources.jar, charsets.jar and class under %JRE_HOME%\lib.
  • Extention ClassLoader is mainly responsible for loading jar packages and class files in the directory %JRE_HOME%\lib\ext.
  • Application ClassLoader, mainly responsible for loading all classes under the classpath of the current application
  • User ClassLoader, user-defined class loader, can load the class file of the specified path

In other words, a user-defined class, such as com.hollis.ClassHollis, will not be loaded by Bootstrap and Extension loaders anyway.

Why do parents need to delegate?

As we mentioned above, because of the strict hierarchical relationship between class loaders, the Java class also has a hierarchical relationship.

In other words, this hierarchical relationship is priority.

For example, a class defined in the java.lang package, because it is stored in the rt.jar, will be entrusted to the Bootstrap ClassLoader and finally loaded by the Bootstrap ClassLoader.

And a user-defined com.hollis.ClassHollis class, he will always be entrusted to Bootstrap ClassLoader, but because Bootstrap ClassLoader is not responsible for loading the class, it will be loaded by the Extension ClassLoader, and the Extension ClassLoader is not responsible for this class The loading will eventually be loaded by the Application ClassLoader.

This mechanism has several advantages.

First, by means of delegation, repeated loading of classes can be avoided . When the parent loader has already loaded a certain class, the child loader will not reload this class.

In addition, security is also guaranteed through the way of parental delegation . Because when Bootstrap ClassLoader is loaded, it will only load the classes in the jar package in JAVA_HOME, such as java.lang.Integer, then this class will not be replaced at will, unless someone runs to your machine and destroys your JDK.

Then, it can prevent someone from loading a custom java.lang.Integer with a destructive function. This can effectively prevent the core Java API from being tampered with.

Is the relationship between "parent and child loader" inheritance?

Many people see such names as parent loader and child loader, and they think that there is an inheritance relationship between class loaders in Java.

Even many articles on the Internet have similar wrong views.

It needs to be clarified here that in the parent delegation model, the parent-child relationship between class loaders is generally not implemented in an inheritance relationship, but instead uses a composition relationship to reuse the code of the parent loader.

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

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

How does parental delegation work?

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

The code for implementing parent 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;
        }
    }

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

1. First check whether the class has been loaded. 2. If it is not loaded, call the loadClass() method of the parent loader to load it. 3. If the parent loader is empty, the startup class loader is used as the parent loader by default. 4. If the parent class fails to load, after throwing ClassNotFoundException, call your own findClass() method to load.

How to actively destroy the parent delegation mechanism?

Knowing the realization of the parental delegation model, it is very simple to destroy the parental delegation mechanism.

Because his parents' delegation process is implemented in the loadClass method, if you want to destroy this mechanism, you can customize a class loader and override the loadClass method so that it does not perform parent delegation.

loadClass()、findClass()、defineClass()区别

There are many methods related to class loading in ClassLoader. LoadClass was mentioned earlier. In addition, there are also findClass and defineClass. So what is the difference between these methods?

  • loadClass()
    • It is the main method for class loading. The default parent delegation mechanism is implemented in this method.
  • findClass()
    • Load .class bytecode based on name or location
  • definclass()
    • Convert bytecode to Class

We need to expand on loadClass and findClass. As we said before, when we want to customize a class loader, and like to break the principle of parent delegation, we will override the loadClass method.

So, what if we want to define a class loader, but don't want to break the parent delegation model?

At this time, you can inherit ClassLoader and override the findClass method. The findClass() method is a new method added by ClassLoader after JDK1.2.

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

This method only throws an exception, there is no default implementation.

After JDK1.2, it is no longer advocated that users directly override the loadClass() method, but it is recommended to implement your own class loading logic into the findClass() method.

Because in the logic of the loadClass() method, if the parent class loader fails to load, it will call its own findClass() method to complete the loading.

So, if you want to define your own class loader and follow the parental delegation model, you can inherit ClassLoader and implement your own loading logic in findClass.

Examples of broken parental delegation

The destruction of the parent delegation mechanism is not unusual. Many frameworks and containers will destroy this mechanism to achieve certain functions.

The first situation to be destroyed is before the parental delegation.

Since the parent delegation model was introduced after JDK 1.2, user-defined class loaders were already in use before this. Therefore, these do not follow the principle of parental delegation.

The second is the case where JNDI, JDBC, etc. need to load the SPI interface implementation class.

The third is to implement hot-swappable hot-deployment tools. In order to make the code take effect dynamically without restarting, the module and the class loader are replaced together to realize the hot replacement of the code.

The fourth is the emergence of web containers such as tomcat.

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

Why do JNDI, JDBC, etc. need to break parental delegation?

In our daily development, most of the time we call those basic classes provided by Java through APIs, which are loaded by Bootstrap.

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

As a typical JDBC service, we usually create a database connection in the following ways:

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

Before the above code is executed, DriverManager will be loaded by the class loader first, because the java.sql.DriverManager class is located under rt.jar, so it will be loaded by the root loader.

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

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

This code will try to load all implementation classes under the classpath that implement the Driver interface.

So, here comes the problem.

DriverManager is loaded by the root loader, so when you encounter the above code when loading, it will try to load all the driver implementation classes, but these implementation classes are basically provided by third parties. According to the principle of parent delegation, third-party classes cannot be The root loader loads.

So, how to solve this problem?

Therefore, the principle of parent delegation was destroyed by introducing ThreadContextClassLoader (thread context loader, AppClassLoader by default) in JDBC.

We can see when we dive into the ServiceLoader.load method:

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

In the first line, get the current thread's thread context class loader AppClassLoader, which is used to load specific implementation classes in the classpath.

Why does Tomcat destroy parental delegation

We know that Tomcat is a web container, so a web container may need to deploy multiple applications.

Different applications may depend on different versions of the same third-party class library, but the full path name of a certain class in different versions of the class library may be the same.

For example, multiple applications must rely on hollis.jar, but application A needs to rely on version 1.0.0, but application B needs to rely on version 1.0.1. A class in both versions is com.hollis.Test.class.

If the default parent-delegated class loading mechanism is adopted, it is impossible to load multiple identical classes.

Therefore, Tomcat breaks the principle of parental delegation, provides an isolation mechanism, and provides a separate WebAppClassLoader loader for each web container.

Tomcat’s class loading mechanism: In order to achieve isolation, priority is given to loading classes defined by the Web application itself, so it does not follow the convention of parental delegation. Each application’s own class loader—WebAppClassLoader is responsible for loading class files in its own directory. It will be loaded by CommonClassLoader later, which is the opposite of parental delegation.

Modularization technology and class loading mechanism

In recent years, modularization technology has been very mature, and modularization technology has been applied in JDK 9.

In fact, as early as before JDK 9, the framework of OSGI was modular. The reason why OSGI can achieve module hot swap and precise control of module internal visibility is due to its special class loading mechanism, between loaders The relationship is no longer the tree structure of the parent delegation model, but develops into a complex network structure.

-w942

In JDK, parental delegation is not absolute.

Before JDK9, the basic classes of JVM used to be in the package rt.jar, which is also the cornerstone of JRE operation.

This is not only a violation of the single responsibility principle, but also many useless classes are also packaged when the program is compiled, causing bloat.

In JDK9, the entire JDK is built based on modularity. The previous rt.jar and tool.jar are split into dozens of modules. When compiling, only the modules actually used are compiled, and each class loader performs its own tasks. Responsible, only load the modules that you are responsible for.

Class<?> c = findLoadedClass(cn);
if (c == null) {
    // 找到当前类属于哪个模块
    LoadedModule loadedModule = findLoadedModule(cn);
    if (loadedModule != null) {
        //获取当前模块的类加载器
        BuiltinClassLoader loader = loadedModule.loader();
        //进行类加载
        c = findClassInModuleOrNull(loadedModule, cn);
     } else {
          // 找不到模块信息才会进行双亲委派
            if (parent != null) {
              c = parent.loadClassOrNull(cn);
            }
      }
}

to sum up

The above, from what is parental delegation, how to achieve and destroy parental delegation, and from the examples of destroying parental delegation, a comprehensive introduction to the knowledge of parental delegation.

I believe that by studying this article, you must have a deeper understanding of the parental delegation mechanism.

After reading this article, I wrote backhand on my resume: Familiar with Java's class loading mechanism, and ask if you are not satisfied!

About the Author: Hollis series of articles the author has a unique quest for Coding people, Alibaba technical experts, "Programmer's three courses," co-author, "Java engineers to God's way."

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

Guess you like

Origin blog.csdn.net/hollis_chuang/article/details/112462198