To prepare

The preparation phase is the phase where you formally allocate memory and set initial values for variables defined in a class (that is, static variables, modified by static). Conceptually, the memory used by these variables should be allocated in the method area, but note that the method area itself is a logical area. In JDK7 and earlier, the permanent generation implements the method section, and in JDK8 and later, class variables are stored in the Java heap along with class objects.

Only class variables, not instance variables, are allocated in the Java heap along with the object when it is instantiated. Suppose a class variable is defined as:

public static int value = 123;
Copy the code

The initial value of the value variable after the preparation phase will be 0 instead of 123, because no Java methods have been executed at this point, and the assignment of value to 123 will not be performed until the class initialization phase.

Here are zero values for each data type:

However, if the field attribute of a class field has a ConstantValue attribute, it is initialized to the specified value during preparation. Such as:

public static final int value = 123;
Copy the code

Javac will generate a ConstantValue attribute for value at compile time, and value will be assigned a value of 123 during the preparation phase.

parsing

The parsing phase is the process by which the Java virtual machine replaces symbolic references in the constant pool with direct references.

Symbolic reference: A symbolic reference describes the referenced object as a set of symbols, which can be any literal, as long as it is used to unambiguously locate the object.

Direct reference: A direct reference is a pointer that can point directly to a target, a relative offset, or a handle that can be indirectly located to the target. Direct references are directly related to the memory layout implemented by the virtual machine.

The resolution action is mainly for the class or interface, field, class method, interface method, method type, method handle, and call point qualifier.

  1. Class and interface resolution

    If you want to parse an unparsed symbolic reference N into a direct reference to a class or interface C, the process consists of the following three steps:

    1.1 If C is not an array type, the virtual machine will pass the fully qualified name of N to D’s classloader to load C. This loading process may trigger the loading action of other related classes, such as the loading of the parent class or the interface implemented by this class. If this process fails, the parsing process will fail.

    1.2 If C is an array type and the element type of the array is object, the descriptor of N will be something like “[Ljava/lang/Integer”. If the element type is primitive, it will be “java.lang.integer”.

    1.3 If no exception occurs in the previous two steps, C is actually a valid class or interface in the virtual machine, but symbolic reference verification is required to determine whether D has access to C before parsing is complete. If it is found that do not have access, will throw Java. Lang. IllegaleAccessError anomalies.

  2. Field analytical

    To resolve an unparsed symbolic reference to a field, the symbolic reference to the class or interface to which the field belongs is first resolved. If successful, the following field search is performed:

    2.1 If C itself contains a field whose simple name and field descriptor match the target, a direct reference to that field is returned, and the search ends.

    2.2 If an interface is implemented in C, each interface and its parent interface will be recursively searched from bottom to top according to the inheritance relationship. If the interface contains a field whose simple name and field descriptor match the target, the direct reference of this field will be returned, and the search ends.

    2.3 If C is not java.lang.Object, it will recursively search the parent class from bottom to top according to the inheritance relationship. If the parent class contains a field whose simple name and field descriptor match the target, it will return a direct reference to the field, and the search ends.

    2.4 otherwise lookup failure, throw the Java. Lang. NoSuchFieldError anomalies.

    If the lookup process successfully returned to the reference, this field will be on authentication, if it is found that do not have access field, will be thrown Java. Lang. IllegalAccessError anomalies.

    If a field with the same name appears in both a class’s interface and its parent, or in multiple interfaces of its own or its parent, the Javac compiler may refuse to compile it as a class file. Public static int A = 4; public static int A = 4; The compiler will prompt “The field sub. A is ambiguous” and refuse to compile this code.

    public class FieldResolution{
    	interface Interface0{
    		int A = 0;
    	}
      interface Interface1 extends Interface0{
        int A =1;
      }
      interface Interface2{
        int A = 2;
      }
      static class Parent implements Interface1{
        public static int A = 3;
      }
      static class Sub extends Parent implements Interface2{
        //public static int A = 4;
      }
      public static void main(String[] args){ System.out.println(Sub.A); }}Copy the code
  3. Method resolution

    The first step in method resolution, like field resolution, is to resolve symbolic references to the class or interface to which the method belongs. If the parse succeeds, then we will still use C for the class.

    3.1 Since the class file format defines the constant type referenced by the class method symbol and the interface method symbol separately, if C in the class_index index is found to be an interface in the class method, That is thrown directly Java. Lang. IncompatibleClassChanggeError anomalies.

    3.2 If there is a method in class C whose simple name and descriptor match the target through the first step, return a direct reference to this method, and the search ends.

    3.3 Otherwise, recursively search the parent class of class C to see if there is a method whose simple name and descriptor match the target. If so, return a direct reference to this method, and the search ends.

    3.4 otherwise, the class C list of implementation of the interface and their parent interface of recursive search whether there is a simple name and descriptor with the target matching method, if there is a match, the method of class C is an abstract class, to find the end, throwing Java lang. AbstractMethodError anomalies.

    3.5 otherwise, declaring the method lookup failure, throw out the Java. Lang. NoSuchMethodError.

  4. Interface method parsing

    A symbolic reference to the class or interface to which the method belongs is first resolved. If the resolution succeeds, the interface is still represented by C.

    4.1 Throw an exception if index C is found to be a class rather than an interface

    4.2 Otherwise, search interface C for a method that matches the target. If there is one, return a direct reference to this method, and the search ends.

    4.3 Otherwise, search recursively in the parent interface of interface C until the java.lang.Object class. If there is a matching method, return this direct reference and the search ends.

    4.4 Because Java interfaces allow multiple inheritance, if C’s different parent interfaces have multiple matching methods, one of those methods is returned and the search ends.

    4.5 otherwise, declaring the method lookup failure, throw Java. Lang. NoSuchMethodError anomalies.

Initialize the

The initialization phase of a class is the last step in the class loading process, where the Java virtual machine actually starts executing the Java program code written in the class.

In the preparation phase, variables have been assigned an initial zero value required by the system, and in the initialization phase, class variables and other resources are initialized according to the program code’s plan: the initialization phase is the execution of the class constructor <clinit> () method. <clinit>() is an automatic artifact of the Javac compiler.

The < Clinit >() method is generated by combining the compiler’s automatic collection of assignment actions for all class variables in the class with statements in the static{} block. The compiler’s collection order is determined by the order in which the statements appear. Only variables defined before the static statement block can be accessed, and variables defined after it can be assigned to, but not accessed, the preceding static statement block. Such as:

public class Test{
	static{
    i = 0;  // Copy variables to compile correctly
    System.out.print(i);  // The compiler will say "illegal forward reference"
  }
  static int i = 1;
}
Copy the code

Unlike class constructors, the <clinit>() method does not explicitly invoke the parent class constructor, and the Java Virtual machine guarantees that the parent class’s <clinit>() method will complete before the subclass’s <clinit>() method executes. So the first <clinit>() method to be executed in the Java virtual machine must be of type java.lang.object.

Since the parent’s <clinit>() method executes first, this means that the parent’s static block takes precedence over the subclass’s variable assignment. For example, the following code field B will be 2 instead of 1:

static class Parent{
	public static int A = 1;
	static{
		A = 2; }}static class Sub extends Parent{
  public static int B = A;
}

public static void main(String[] args){
  System.out.println(Sub.B);
}
Copy the code

The <clinit>() method is not required for a class or interface, and the compiler may not generate a <clinit>() method for a class that has no static blocks and no assignment to variables.

Class loader

The action “get the binary stream describing a class by its fully qualified name” is implemented externally to the Java virtual machine so that applications can decide how to get the required classes. The code that implements this action is called the classloader.

For any class, the uniqueness of the Java virtual machine must be established by both the classloader that loads it and the class itself. Each classloader has a separate class namespace. In other words, comparing two classes to whether they are “equal” only makes sense if they are loaded by the same classloader.

Parental delegation model

Bootstrap Class Loader: the Bootstrap Class Loader is stored in the <JAVA_HOME>/lib directory. The startup class loader cannot be referenced directly by a Java program.

The Extension Class Loader is responsible for loading the <JAVA_HOME>/lib/ext directory or all Class libraries specified by the java.ext.dirs system variable. Developers can load class files directly in their programs using the extended class loader.

The Application Class Loader is responsible for loading all Class libraries on the user’s classPath. If there is no custom class loader in the application, this is generally the default class loader for the application.

The working process of the parental delegation model is: If a classloader receives a classload request, it first does not attempt to load the class itself. Instead, it delegates the request to the parent classloader. This is true at every level of classloaders, so all load requests should eventually be sent to the top level of the starting classloader. Only when the parent loader reports that it cannot complete the load request (it did not find the desired class in its search scope) will the child loader attempt to complete the load itself.

The benefit of this loading pattern is that classes in Java have a hierarchical relationship with priority along with their classloaders. For example, the java.lang.Object class, which is stored in rt.jar, is ultimately delegated to the boot class loader at the top of the model by whichever class loader loads the class, so that the Object class is guaranteed to be the same class in every class loading environment of the program.

Implementation of the parent delegate model:

protectedsynchronzied Class<? > loadClass(String name,boolean resolve) throws ClassNotFoundException{
  // First check if the requested class has already been loaded
  Class c = findLoadedClass(name);
  if(c == null) {try{
      if(parent ! =null){
        c = parent.loadClass(name,false);
      }else{ c = findBootstrapClassOrNull(name); }}catch(ClassNotFoundException e){
      // If the parent class loader throws a ClassNotFoundException
      // Indicates that the parent class cannot complete the load request
    }
    if(c == null) {// When the parent class loader cannot be loaded
     // Call its own findClass method to loadc = findClass(name); }}if(resolve){
    resolveClass(c);
  }
  return c;
}
Copy the code

The logic of this code is to check whether the requested type has already been loaded. If not, the parent loader’s loadClass() method is called. If the parent loader is empty, the startup class loader is used as the parent loader by default. If the parent class loader fails to load, you call your own findClass() method to try loading.

Tomcat’s class loader

Tomcat’s custom class loader, WebAppClassLoader, breaks the parent delegation mechanism. It first tries to load a class by itself, and if it cannot find the parent class loader, its purpose is to load the classes defined by the Web application first. The implementation is to override two ClassLoader methods: findClass and loadClass.

Here is the code implementation of findClass:

publicClass<? > findClass(String name)throwsClassNotFoundException { ... Class<? > clazz =null;
    try {
            //1. First look for the class in the Web application directory
            clazz = findClassInternal(name);
    }  catch (RuntimeException e) {
           throw e;
       }
    
    if (clazz == null) {
    try {
            //2. If it is not found in the local directory, the parent loader will look for it
            clazz = super.findClass(name);
    }  catch (RuntimeException e) {
           throw e;
       }
    
    //3. If the parent class is not found, throw ClassNotFoundException
    if (clazz == null) {
        throw new ClassNotFoundException(name);
     }

    return clazz;
}
Copy the code

In the findClass method, there are three main steps:

1. Find the class to be loaded in the local directory of the Web application.

2. If it does not find it, it is handed over to the parent loader, which is the AppClassLoader mentioned above.

3. If the parent loader also does not find the class, raise ClassNotFound.

The loadClass method:

publicClass<? > loadClass(String name,boolean resolve) throws ClassNotFoundException {

    synchronized(getClassLoadingLock(name)) { Class<? > clazz =null;

        1. Check whether the class has been loaded in the local cache
        clazz = findLoadedClass0(name);
        if(clazz ! =null) {
            if (resolve)
                resolveClass(clazz);
            return clazz;
        }

        //2. Check the system class loader cache to see if it has been loaded
        clazz = findLoadedClass(name);
        if(clazz ! =null) {
            if (resolve)
                resolveClass(clazz);
            return clazz;
        }

        // 3. Try using ExtClassLoader to load classes. Why?
        ClassLoader javaseLoader = getJavaseClassLoader();
        try {
            clazz = javaseLoader.loadClass(name);
            if(clazz ! =null) {
                if (resolve)
                    resolveClass(clazz);
                returnclazz; }}catch (ClassNotFoundException e) {
            // Ignore
        }

        // 4. Try to search for class in the local directory and load it
        try {
            clazz = findClass(name);
            if(clazz ! =null) {
                if (resolve)
                    resolveClass(clazz);
                returnclazz; }}catch (ClassNotFoundException e) {
            // Ignore
        }

        // 5. Try using the system class loader (i.e. AppClassLoader) to load it
            try {
                clazz = Class.forName(name, false, parent);
                if(clazz ! =null) {
                    if (resolve)
                        resolveClass(clazz);
                    returnclazz; }}catch (ClassNotFoundException e) {
                // Ignore}}//6. The preceding processes failed to load, and an exception was thrown
    throw new ClassNotFoundException(name);
}
Copy the code

The loadClass method is a little more complicated, with six main steps:

  1. The local Cache checks whether the class has been loaded, that is, whether the Tomcat class loader has loaded the class.
  2. If the Tomcat class loader has not loaded the class, see if the system class loader has.
  3. If none is available, have the ExtClassLoader load it. This step is critical to prevent the Web application’s own classes from overwriting the core JRE classes. Because Tomcat needs to break the parental delegation mechanism, if you have a custom class called Object in your Web application, if you load the Object class first, it will overwrite the Object class in the JRE. This is why the Tomcat class loader preferentially tries to load using ExtClassLoader because ExtClassLoader delegates to BootstrapClassLoader, When the BootstrapClassLoader finds that it has loaded the Object class, it directly returns it to the Tomcat class loader. In this way, the Tomcat class loader does not load the Object class in the Web application, and avoids the problem of overwriting the core JRE class.
  4. If the ExtClassLoader fails to load, that is, it does not exist in the JRE core class, look it up in the local Web application directory and load it.
  5. If the class is not in the local directory, it is not defined by the Web application itself. In this case, the system class loader loads the class. Note here that the Web application is delivered to the system Class loader through the class.forname call, because the default loader for class.forname is the system Class loader.
  6. If all of the above loading fails, a ClassNotFound exception is thrown.

As can be seen from the above process, the Tomcat class loader breaks the parent delegate mechanism. Instead of directly delegating to the parent loader, the Tomcat class loader is loaded in the local directory first. In order to avoid the classes in the local directory overwriting the core classes of JRE, First try using the JVM extension class loader ExtClassLoader to load. Why not use the system class loader AppClassLoader first? Obviously, if this is the case, then it becomes a parent delegate mechanism, which is the cleverness of the Tomcat class loader.

The Hierarchy of Tomcat class loaders

As we know, Tomcat, as the Servlet container, is responsible for loading our Servlet class, as well as the JAR packages on which the Servlet depends. And Tomcat itself is a Java program, so it needs to load its own classes and dependent JAR packages. First, let’s consider these questions:

  1. If we run two Web applications in Tomcat, and two Web applications have servlets with the same name but different functions, Tomcat needs to load and manage these two Servlet classes at the same time to ensure that they do not conflict, so the classes between the Web applications need to be isolated.
  2. If two Web applications rely on the same third-party JAR package, such as Spring, Tomcat must ensure that the two Web applications can share the JAR package after the Spring JAR package is loaded into memory, that is, the Spring JAR package is loaded only once. Otherwise, the MEMORY of the JVM will swell as the number of dependent third-party JAR packages increases.
  3. As with the JVM, we need to separate the classes of Tomcat itself from the classes of the Web application.

If we use the JVM default AppClassLoader to load a Web application, AppClassLoader can load only one Servlet class. When loading a second Servlet class with the same name, AppClassLoader only returns the Class instance of the first Servlet Class, because in AppClassLoader’s view, a Servlet with the same name can only be loaded once.

So Tomcat’s solution is to customize a WebAppClassLoader and create an instance of the class loader for each Web application. We know that the Context container component corresponds to a Web application, so each Context container is responsible for creating and maintaining a WebAppClassLoader instance. The principle behind this is that classes loaded by different loader instances are considered to be different classes. This is equivalent to creating isolated Java class Spaces inside the Java VIRTUAL machine. Each Web application has its own class space and Web applications are isolated from each other through their own class loaders.

SharedClassLoader

Turning to the second issue, the essential requirement is how to share library classes between two Web applications and not load the same classes twice. In the parent delegate mechanism, each child loader can load classes through the parent loader, so it is ok to put the shared classes in the parent loader load path, and applications share the core JRE classes in this way. Therefore, Tomcat designers added a class loader, SharedClassLoader, as the parent of WebAppClassLoader, specifically to load classes shared between Web applications.

CatalinaClassLoader

Look at the third problem, how to separate Tomcat classes from web application classes. We know that sharing can be done through parent-child relationship, and isolating requires sibling relationship. A sibling relationship is when two class loaders are parallel. They may have the same parent, but the classes loaded by the two sibling classloaders are isolated. Based on this, Tomcat designs a class loader CatalinaClassLoader to load Tomcat classes.

CommonClassLoader

Another question: What about classes that need to be shared between Tomcat and various Web applications? Add CommonClassLoader as parent of CatalinaClassLoader and SharedClassLoader. Classes that can be loaded by CommonClassLoader can be used by CatalinaClassLoader and SharedClassLoader.