Learning the JVM from Scratch series:

Learning from the Ground up ->JVM (Preface)

Learning from the ground up ->JVM (Preface) – Why delay?

JVM (a) : The Java Memory Model (JMM) is not the Java Virtual Machine memory model (JVM) oh!

Learning from the Ground up – JVM part 2: Why does Java Need a JVM (Java Virtual Machine)?

Learning from the Ground up ->JVM (3) : Classloader (1)

Learning from the Ground up ->JVM (4) : Classloader (Middle)

Learning from the ground up ->JVM (5) : Classloader (2)

Learning from the Ground up – JVM part 6: The Relationship between threads and JVMS

Learning from the ground up ->JVM (7) : Runtime Data Area (1)

Learning from the ground up ->JVM (8) : Runtime Data area (2)

Learning from scratch ->JVM (9) : Garbage Collection (1)

Learning from the ground up ->JVM (10) : Garbage Collection (middle)

Learning from scratch ->JVM (11) : Garbage Collection (2)

preface

In the last article, we learned that the loading process of a class consists of five phases: load, validate, prepare, parse, and initialize. We have also taken a more detailed look at these five phases, and in the course of this reading, we found that the classloader is mainly responsible for the loading phase. Of course, we didn’t know much about classloaders at the time, but this article will focus on classloaders.

Before the start of the article, we need to understand one thing, if we are to the class loader has a profound understanding of, also do not represent the entire process of class loading we have how much cognitive, this can only say that we in the JVM the vast ocean, make a small a ladle of water, small, and diffusing. All we have to hold a modest attitude, to keep learning, always progress.

Now that we’re done diverging, let’s get into the world of class loaders.

The origin of class loaders

Classloaders have been around since jdk1.0 and were originally developed to meet the needs of Java applets.

Applets can be translated into small applications. Java applets are small applications written in the Java language that can be embedded directly into web pages and have special effects. A web page that contains an Applet is called a Java-powered page, and you can call it a Java-enabled page.

Therefore, Java applets need to download Java class files from the remote network to the browser and execute them. How do you ensure that Java class files are legal and compliant? Java then creates the Applet class loader to load Java files. The applet class loader ensures that classes from the file system have unique namespaces, and classes from network resources have unique namespaces.

When a browser loads an applet over the network, the applet’s classes are placed in a private namespace associated with the applet’s source. Then, classes that are loaded by the classloader are validated by the validator. The validator checks that the class file format complies with the Java language specification, ensures that there is no stackoverflow or underflow, and that the parameters passed to the bytecode instruction are correct.

Java applets for today’s browsers are largely obsolete, but classloaders have emerged as an important cornerstone of the Java technology architecture in the areas of class hierarchy, OSGi, application hot deployment, code encryption, and so on.

The role of the class loader

What is a class loader? From my personal understanding, class loaders are used to load Java classes into the JVM.

A classloader loads a class file into a virtual machine, using the fully qualified name of a class to retrieve a stream of binary bytes describing that class.

It is important to mention here what is a fully qualified name for a Java class. Take the String class we use most often. Its fully qualified name is "java.lang.String", that is, the fully qualified name of a Java class is: package name + class name. This corresponds to Java unqualified class names, also known as short names, that we call class names, such as String.Copy the code

Typically, Java source programs, or Java files, are converted to Java bytecodes after being compiled by a Java compiler (.class files are called bytecodes, represented by the byte array byte[]), and our classloader reads the Java bytecodes. Convert to an instance of the java.lang.Class Class and load the Class object into virtual machine memory.

What is the Java.lang. Class Class?

One of the basic ideas of Java is that everything is an object, and a class is an object. But what object is a class? Classes in Java are instantiated objects of java.lang.Class, which are called Class types.

At runtime, the Java runtime system consistently identifies all objects as runtime types. This information records the class to which each object belongs. The virtual machine usually uses runtime type information to select the correct method to execute, and the Class used to hold this type information is the Class Class. The Class Class encapsulates the state of an object and interface at runtime. When a Class is loaded, objects of Class type are created automatically.

Class has no public constructor. Class objects are automatically constructed by the Java Virtual machine when the Class is loaded and by calling the defineClass method in the Class loader, so you cannot explicitly declare a Class object.

The virtual machine manages a unique Class object for each type. That is, each Class (type) has a Class object. When running a program, the Java Virtual Machine (JVM) first checks to see if the Class object corresponding to the Class to be loaded has already been loaded. If it is not loaded, the JVM looks for the.class file based on the class name and loads its class object.

When a class needs to be used (so it’s important to remember that in Java, classes are only loaded on demand), the class loader loads its “.class” file and creates the corresponding class object to load the class file into the virtual machine’s memory.

Thus, each such instance is used to represent a Java class, of which an object can be created through the newInstance() method of the instance.

Although the class loader is only used to implement the loading action of the class, it plays a far more important role in Java programs than the class loading phase. 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. This sentence can express more popular: compare whether the two classes “equal”, only in these two classes are from the same Class loader under the premise of load to be meaningful, otherwise, even if these two classes derived from the same Class files, by the same Java virtual machine loading, as long as the load they’re different Class loaders, these two classes are not necessarily equal.

The type of class loader

From the perspective of Java virtual machines, there are only two different class loaders: one is the Bootstrap ClassLoader, which is implemented in C++ and is part of the virtual machine itself; The other is all the other classloaders, which are implemented in the Java language, exist independently of the virtual machine, and all inherit from the abstract java.lang.classloader class.

But, in general, we can put the class loader points a little bit more detailed, in our developer point of view, in the JVM, class loading generally divided into three kinds, then in addition to the JVM bring three kinds of loader, and our users themselves can create the development of class loaders, from this perspective, there are four class loader is. So what are the four types of class loaders?

1. Bootstrap Class Loader

The boot class loader is part of the virtual machine. It is written in C++ and cannot see the source code. It is part of the Java virtual machine, while the other class loaders are written in Java and can see the source code.

This class loader is responsible for loading files stored in <JAVA_HOME>\lib, or in the path specified by the -xbootclasspath parameter, that are recognized by the Java virtual machine (by filename, e.g. Rt.jar, tools.jar, Libraries with incorrect names will not be loaded even if placed in the lib directory.) The libraries are loaded into the virtual machine memory.

The startup class loader cannot be directly referenced by Java programs. If you need to delegate the loading request to the startup class loader when writing a custom class loader, you can use NULL instead.

2. Extention Class Loader

Extended class loaders are those implemented by the JVMsun.misc.LauncherThe $ExtClassLoader class, implemented in the Java language, is a static inner class for the Launcher.


Dirs directory, or from the JDK installation directory: jre/lib/ext. It is a subclass of the startup class loader.

This is a Java system library extension mechanism that the JDK development team allowed users to extend Java SE functionality by placing general-purpose libraries in ext directories. After JDK9, this extension mechanism was replaced by the natural extension capabilities of modularity. Since the extension classloader is implemented in Java code, developers can use the extension classloader directly in their programs to load Class files.

3. Application Class Loader

Java language, implemented by sun.misc.Launcher$AppClassLoader. Derived from java.lang.ClassLoader, the parent ClassLoader is the launcher ClassLoader.


It is responsible for loading the environment variable classpath or system property java.class.path under the specified path of the class library, it is the default class loader in the program, our Java program classes, are loaded by it. We can get and manipulate this loader by using ClassLoader#getSystemClassLoader().

Because the application ClassLoader is the return value of the getSystemClassLoader() method in the ClassLoader class, it is also called the “system ClassLoader” in some cases. It is responsible for loading all libraries on the user’s ClassPath, and developers can use the class loader directly in their code. If the application does not have its own custom class loader, this is generally the default class loader in the application.

4. Custom User Class Loader

Java applications prior to JDK9 were loaded with a combination of these three types of loaders, and users could extend this by defining their own class loaders if they felt the need.

In general, the purpose of adding your own class loaders is as follows (not all cases, just examples) :

  1. Add Class file sources other than disk locations.
  2. Through the class loader to achieve class isolation, overload and other functions.
  3. When a Java class is loaded on the network, encryption operation is adopted to ensure the security of transmission. Therefore, the above three loaders cannot load this class. In this case, a custom loader is required.

So, how do you customize class loaders?

  1. Inherit the java.lang.classloader class and override the findClass() method
  2. If you don’t have too complicated requirements, you can simply inherit the URLClassLoader class and override the loadClass method. See AppClassLoader and ExtClassLoader for details. (Note that this breaks the parental delegation model as I’ve illustrated in the screenshot above.)

When is the class loaded

  1. Create an instance of the class, that is, new an object
  2. Accesses or assigns a value to a static variable of a class or interface
  3. Call a static method of a class
  4. Reflection (class.forname (“com.lyj.load”))
  5. Initialize a subclass of a class (initializes the subclass’s parent class first)
  6. The startup class that the JVM identifies when it starts, that is, the class with the same file name and class name

In addition, the following situations need to be pointed out:

A static variable of final type (as mentioned in the previous article learning the JVM from Scratch: Classloader, Part 1) is equivalent to a “macro variable” if its value can be determined at compile time. The Java compiler replaces the value of the variable directly where it appears at compile time, so even if the program uses the static variable, it does not cause the class to be initialized. Conversely, if the value of a static Field of a final type cannot be determined at compile time, the value of the variable must be determined at run time, and if its static variable is accessed through the class, the class will be initialized.

Parental delegation model

The parent delegate model is a matter of setting the priority order of loader-dependent loader-loading classes.

Speaking of the parental delegation model, let’s start with a super classic diagram. Figure is as follows:As we can see from the figure, loaders must be loaded in a sequential order, and this order is the parent delegate model.

So, what is the parental delegation model?

If a classloader receives a classload request, it does not try to load the class itself at first. 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.

So what are the benefits of using the parental delegation model?

  1. Avoid reloading. When the parent has loaded the class, there is no need for the subclass loader to load again, resulting in a waste of resources, but also to ensure the uniqueness of the Java core API loaded classes, to ensure the basic behavior and logic of the program.
  2. Ensure that the Java core API will not be arbitrarily replaced, to ensure a certain degree of security. Suppose we pass a class named java.lang.Integer over the network, pass it through the parent delegate model to the bootstrap loader, and the bootstrap loader finds the class with that name in the core Java API and finds that the class has already been loaded. The class is never loaded again, which ensures that Java’s core API library can be tampered with at will.

And, of course, the JVM is not delegate parents mechanism as a binding constraint rules, this is only a recommended by the Java class loading way, in most of the time, this model is to meet all of the user’s request, but there are always some time, we will find that this model may not be able to meet our requirements.

For example, the parent delegate model may not meet our requirements if:

  1. Code hot replacement.
  2. Hot module deployment.
  3. Code is required to invoke the JNDI Service Provider Interface (SPI) implemented by another vendor and deployed in the application’s ClassPath.

Of course, all of these features need to be implemented specifically, and it is a matter of opinion, but the JVM has already solved the third point in JDK6 by providing the java.util.ServiceLoader class with the configuration information in meta-INF /services, Coupled with the chain of responsibility pattern, this provides a relatively reasonable solution for the loading of these classes.

But if point 1 and point 2 involve hot updates and hot deployment, then we have to learn to break the parent delegate model.

So the question is, how do you break the parental delegation model?

Here I will give you two examples:

  1. Native JDBC Driver Driver itself is only an interface, and there is no specific implementation, specific implementation is by different database types to achieve. For example, MySQL implements the Driver class in mysql-connector-.jar. In JDBC, the Driver class needs to be loaded dynamically for different database types. In mysql-connector-.jar, the Driver class is written by the user. The startup class loader is definitely not able to load, since you write your own code, it needs to be started by the application class to load the class. This is where the Thread Context ClassLoader is introduced. With this in place, the application class loader loads classes that would otherwise be loaded by the launcher class loader.

  2. The key to OSGi’s modular hot deployment is the implementation of its custom classloader mechanism. Each program module (Bundle) has its own class loader. When a Bundle needs to be replaced, the Bundle is replaced with the same class loader to achieve hot replacement of the code. In the OSGi fantasy, class loaders are no longer the tree structure of the parent delegate model, but evolve into a more complex network structure. When a class load request is received, OSGi searches for classes in the following order:

1.1 Delegate classes starting with Java. * to the parent class loader. 1.2 Otherwise, delegate the classes in the delegate list to the parent class loader. 1.3 Otherwise, delegate the classes in the Import list to the class loader of the Export class's Bundle. 1.4 Otherwise, find the current Bundle's ClassPath and load it using your own class loader. 1.5 Otherwise, check whether the class is in the Fragment Bundle. If so, delegate the load to the class loader of the Fragment Bundle. 1.6 Otherwise, find the Bundle in the Dynamic Import list and delegate the load to the corresponding Bundle's classloader. 1.7 Otherwise, the classloader fails.Copy the code

What we found in these two examples is that there are two ways to break the parental delegation mechanism:

  1. Custom class loaders that override the loadClass method.
  2. Use the thread context class loader.

What is a thread context classloader?

The Thread Context Class Loader was introduced in JDK 1.2. The methods getContextClassLoader() and setContextClassLoader(ClassLoader CL) in the java.lang.Thread class are used to get and set the context ClassLoader for the Thread. If not set by the setContextClassLoader(ClassLoader CL) method, the thread inherits its parent thread’s context ClassLoader. The context classloader for the initial thread on which a Java application runs is the system classloader. Code running in a thread can load classes and resources through this type of loader.

Proxy mode for the class loader

When a class loader tries to find the bytecode of a class and define it, it proxies for its parent class loader, which tries to load the class, and so on.

Before I get into the motivation behind the proxy pattern, I need to explain how the JVM determines that two Java classes are the same.

When we talked about the parent delegate mechanism above, we learned that the JVM looks not only at whether the full name of a class is the same, but also whether the class loader that loaded the class is the same. Two classes are considered identical only if they are identical. Even if the same bytecode is loaded by different classloaders, the resulting classes are different.

For example, a Java class com.example.Sample is compiled to generate the bytecode file sample.class. Two different classloaders, ClassLoaderA and ClassLoaderB, read the sample. class file and define two instances of the java.lang. class class to represent the class. The two instances are not identical. To the Java Virtual machine, they are different classes. Attempting to assign objects of these two classes to each other throws a ClassCastException at runtime.

With this in mind, you can understand the design motivation for the proxy pattern.

The proxy pattern is intended to keep the Java core libraries type safe. All Java applications need to reference at least the Java.lang. Object class, meaning that the java.lang.Object class needs to be loaded into the Java virtual machine at runtime. If the loading process is done by the Java application’s own classloader, it is likely that there are multiple versions of the Java.lang. Object class that are incompatible with each other. Through the proxy mode, the loading of Java core library classes is uniformly completed by the bootstrap class loader, which ensures that Java applications are using the same version of Java core library classes and are compatible with each other.

Different class loaders create additional namespaces for classes with the same name. Classes with the same name can coexist in the Java virtual machine, simply by loading them with different class loaders. Classes loaded by different class loaders are incompatible, which creates separate Java class Spaces within the Java virtual machine.

conclusion

When we learn about the class loader, we will find that we are obviously very unfamiliar with the Java underlying code. It is important to realize that the JVM is just a concept, and that it is our underlying Java code that implements the JVM.

For example, our ExtClassLoad and AppClassLoad loaders are static inner classes of Java’s underlying class Launcher.

Therefore, if we have only a few theories of the JVM in mind, we have only a superficial understanding of the JVM, not a very deep one.

In the next article, Learning the JVM from The Ground up: Classloaders part 2, I will try to explain the underlying code for classloaders and write my own class loaders.

Refer to the blog

Blog.csdn.net/m0_38075425…

Blog.csdn.net/javazejian/…

Developer.ibm.com/zh/articles…

Reference books

Understanding the Java Virtual Machine by Zhiming Zhou