Welcome to github.com/hsfxuebao/j… , hope to help you, if you feel ok, please click on the Star

From www.cnblogs.com/xjwhaha/p/1…

1. An overview of the

Class loaders are a prerequisite for the JVM to implement a classloading mechanism

The ClassLoader is used for:

ClassLoader is the core component of Java. All classes are loaded by the ClassLoader. ClassLoader is responsible for reading the binary data stream of Class information into the JVM in various ways. Convert to an instance of a Java.lang. Class object corresponding to the target Class. The Java virtual machine then performs linking, initialization, and other operations. Therefore, during the whole loading phase, ClassLoader can only affect the loading of the class, and it cannot change the linking and initialization behavior of the class through ClassLoader. Whether or not it can run is up to the Execution Engine

Class loading:

Classloaders first appeared in Java 1.0, when they were developed solely for Java Applet applications, but today they are taking off in OSGI, bytecode encryption and decryption. This is largely due to the fact that the Java Virtual machine designers did not consider binding the class loader inside the JVM when they designed it, which would have the advantage of being more flexible and dynamic for class loading operations

Classification of class loading

Class load classification:

Explicit loading vs implicit loading

Explicit and implicit loading of a Class file refers to how the JVM loads a Class file into memory

  • Explicit loading refers to loading Class objects in code by calling ClassLoader, For example, use class.forname (name) or this.getClass().getClassLoader().loadClass() to load the Class object directly
  • Implicit loading refers to the automatic loading of a Class object into the memory by the VM instead of directly calling the ClassLoader method in the code. For example, when a Class file of a Class is loaded, the Class file of that Class references an object of another Class. At this point the extra referenced classes are automatically loaded into memory by the JVM

In daily development, these two methods are often mixed

The need for class loaders

In general, Java developers do not need to use class loaders explicitly in their programs, but the loading mechanism for unpacking class loaders is crucial. From the following aspects:

  • Avoid meeting in the openingjava.lang.ClassNotFoundExceptionExceptions orjava.lang.NoClassDeFoundErrorDo not know what to do when abnormal. Only by understanding the loading mechanism of the class loader can we quickly locate and resolve problems according to the error exception log when exceptions occur
  • When you need to support dynamic loading of classes or need to encrypt and decrypt compiled bytecode files, you need to deal with class loaders
  • Developers can write custom class loaders in their programs to redefine the loading rules of classes in order to implement some custom processing logic

The namespace

What is unique about a class?

For any class, its uniqueness in the Java virtual machine needs to be confirmed both by the classloader that loads it and by the class itself. Each class loader has a separate class namespace: comparing two classes for equality makes sense only if they are loaded by the same class loader. Otherwise, even if two classes come from the same Class file and are loaded by the same virtual machine, as long as they are loaded by different classloaders, the two classes must not be equal

The namespace

  • Each class loader has its own namespace, which consists of classes loaded by all the parents of the loader
  • No two classes in the same namespace have the same full name (including the package name of the class)
  • In different namespaces, it is possible to have two classes with the same full name (including the package name of the class)

In large applications, we often use this feature to run different versions of the same class

Code example: Two different classloader examples, fetching the Class object of the same Class, are different

public class User { private int id; @Override public String toString() { return "User{" + "id=" + id + '}'; }} Public class UserClassLoader extends ClassLoader {private String rootDir; public UserClassLoader(String rootDir) { this.rootDir = rootDir; } /** * Override protected Class<? > findClass(String name) throws ClassNotFoundException {// Obtain the class file byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); Return defineClass(name, classData, 0, classdata.length); return defineClass(name, classData, 0, classdata.length); Private byte[] getClassData(String className) {// getClassData(String className) {// String path = classNameToPath(className); try { InputStream ins = new FileInputStream(path); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; While ((len = ins.read(buffer))! = -1) { baos.write(buffer, 0, len); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } private String classNameToPath(String className) {return rootDir + "\\" + classname.replace ('.', '\\') + ".class"; } public static void main(String[] args) {String rootDir = "D:\ dev\ workspace\ demo\ code \JVMDemo1\ chapter04\ SRC \\"; Try {// create custom class loader1 UserClassLoader loader1 = new UserClassLoader(rootDir); Class clazz1 = loader1.findClass("com.atguigu.java.User"); UserClassLoader loader2 = new UserClassLoader(rootDir); Class clazz2 = loader2.findClass("com.atguigu.java.User"); System.out.println(clazz1 == clazz2); // Clazz1 and clazz2 correspond to different class template structures. System.out.println(clazz1.getClassLoader()); System.out.println(clazz2.getClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); }}}Copy the code

Basic features of the class loading mechanism

Classloading mechanisms in general have three basic characteristics:

  • Parental delegation model. But not all class loading to comply with this model, sometimes, to start the class loader the type of load, it is possible to load the user code, than if the JDK ServiceProvider/ServiceLoader mechanism of internal, Users can provide their own implementations on top of the standard API framework, and the JDK needs to provide some default reference implementations. For example, many aspects of Java such as JNDI, JDBC, file systems, ciphers, and so on make use of this mechanism, which is not loaded using the diphilophile delegate model, but using what is called a context loader
  • Visibility. The subclass loader can access the type loaded by the parent loader, but the reverse is not allowed. Otherwise, there would be no way to implement container logos using class loaders because of the lack of necessary isolation
  • Unitary. Since the type of the parent loader is visible to the child loader, a type that has been loaded in the parent loader will not be reloaded in the child loader. Note, however, that the same type can still be loaded multiple times between classloader “neighbors” because they are not visible to each other

Back to the top

2. Loader classification of classes

The JVM supports two types of class loaders: Bootstrap ClassLoader and User-defined ClassLoader.

A custom ClassLoader is conceptually a class of class loaders defined by a developer in a program, but the Java Virtual Machine specification does not define it this way. Instead, it classifies all classloaders derived from the abstract ClassLoader as custom classloaders. Regardless of the class loader type, the most common class loader structure in our programs is the following:

  • All class loaders, except the top-level start class loader, should have their own “parent class” loader
  • Different class loaders seem to be Inheritance relationship, actually is inclusion relationship. In the lower loader, a reference to the upper loader is contained

Brief code:

class ClassLoader { ClassLoader parent; Public ClassLoader(ClassLoader parent) {this.parent = parent; } } class ParentClassLoader extends ClassLoader { public ParentClassLoader(ClassLoader parent) { super(parent); } } class ChildClassLoader extends ClassLoader { public ChildClassLoader(ClassLoader parent) { //parent = new ParentClassLoader(); super(parent); }}Copy the code

2.1: Bootstrap the classloader

Start the classloader (bootstrap the classloader)

  • This class loading is implemented in C/C++ and is nested within the JVM
  • It is used to load Java core libraries (JAVA_HOME/jre/lib/rt.jar or the contents of sun.boot.class.path). Used to provide classes that the JVM itself needs
  • Does not inherit from java.lang.ClassLoader and has no parent loader
  • For security reasons, Bootstrap starts the class loader with a package that starts with Java, Javax, and Sun
  • Loads extension classes and application classloaders and specifies them as parent classloaders

You can use-XX:+TraceClassLoadingArgument to print the loading process of the class

2.2: Extended class loaders

Extension ClassLoader

  • Java language, implemented by sun.misc.Launcher$ExtClassLoader
  • Inherits from the ClassLoader class
  • The parent class loader is the initiator class loader
  • Load the class libraries from the directory specified by the java.ext.dirs system property, or from the JRE /lib/ext subdirectory of the JDK installation directory. If user-created jars are placed in this directory, they will also be automatically loaded by the extended class loader

2.3 System class loader

Application class loader (System class loader, AppClassLoader)

  • Java language, implemented by sun.misc.Launcher$AppClassLoader
  • Inherits from the ClassLoader class
  • The parent class loader is the extension class loader
  • It is responsible for loading the class libraries under the path specified by the environment variable classpath or the system property java.class.path
  • The class loader in an application is the system class loader by default
  • It is the default parent of the user-defined class loader
  • The ClassLoader can be obtained by using the getSystemClassLoader() method of the ClassLoader

2.4 User-defined class loaders

  1. In everyday Java application development, class loading is almost performed by the above three types of loaders. If necessary, we can also customize class loaders to customize how classes are loaded
  2. One of the key elements of the vitality and charm of the Java language is that Java developers can use custom class loaders to dynamically load class libraries, either from local JAR packages or from remote resources on the network
  3. There are plenty of examples of great plug-in mechanisms that can be implemented through class loaders. For example, the well-known OSGI component framework, or Eclipse’s plug-in mechanism. Class loaders provide a mechanism for dynamically adding new functionality to an application without repackaging the distribution application
  4. At the same time, custom loaders can realize application isolation. For example, middleware and component frameworks such as Tomcat and Spring implement custom loaders internally and isolate different component modules through custom loaders. This mechanism is so much better than C/C++ programs that it is almost impossible to add functionality to a C/C++ program without modifying it, and a single compatibility prevents all good ideas
  5. Custom classloaders usually need to be inherited from classloaders

2.5 Test different class loaders

Each Class object contains a way to get a ClassLoader by referring to the ClassLoader that defines it

How to obtain each loader:

/ / get the class loader system this systemClassLoader. = this getSystemClassLoader (); System.out.println(systemClassLoader); / / sun. Misc. 18 b4aac2 / / get the Launcher $AppClassLoader @ extension class loader this extClassLoader = systemClassLoader. The getParent (); System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@1540e19d // Try to get the boot class loader: ClassLoader bootstrapClassLoader = extClassloader.getparent (); System.out.println(bootstrapClassLoader); //nullCopy the code

Description:

From a program point of view, the bootloader is not the same class loader as the other two classes (system class loaders and extension class loaders), which are written in C++ and written in Java. Since the boot classloader is not a Java class at all, only null values can be printed in a Java program

Array class loader:

Public class ClassLoaderTest1 {public static void main(String[] args) {public static void main(String[] args) { arrStr = new String[10]; System.out.println(arrStr.getClass().getClassLoader()); //null: bootloader ClassLoaderTest1[] arr1 = new ClassLoaderTest1[10]; System.out.println(arr1.getClass().getClassLoader()); //sun.misc.Launcher$AppClassLoader@18b4aac2 int[] arr2 = new int[10]; System.out.println(arr2.getClass().getClassLoader()); System.out.println(thread.currentThread ().getContextClassLoader())); } catch (ClassNotFoundException e) { e.printStackTrace(); }}}Copy the code

Description:

Class objects of array classes are not created by the Class loader, but are created automatically by the JVM during the Java runtime as needed. The Class loader for an array Class is returned by class.geetClassloader (), the same as the classloader for the element types in an array: If the element types in an array are primitive data types, there is no classloader for an array Class, because primitive types do not need to be loaded by a loader

Back to the top

3. Class loader source analysis

The relationship between ClassLoader and existing class loading:

In addition to the loaders provided by the above virtual machines, users can customize their own class loaders. Java provides the abstract java.lang.ClassLoader class, which should be inherited by all user-defined classloaders

The main method of ClassLoader

The main methods of the abstract class ClassLoader :(there are no internal abstract methods)

  • public final ClassLoader getParent()

    Returns the “parent” loader of the class loader

  • public Class loadClass(String name) throws ClassNotFoundException

    Load a Class with the full Class name of Name that returns an instance of the java.lang.Class Class. If no class is found, the ClassNotFountException exception is returned. The logic in this method is the implementation of the parent delegate pattern

    Source code: reflects the parent delegate mechanism

    // The var2 parameter indicates whether parsing is required. The default is false protected Class<? > loadClass(String var1, Boolean var2) throws ClassNotFoundException {synchronized(this.getClassloadingLock (var1)) { Class var4 = this.findLoadedClass(var1); If (var4 == null) {long var5 = system.nanotime (); Try {// If the parent loader is not empty, then the parent loader is called recursively, and if nothing is returned, then continue if (this.parent! = null) { var4 = this.parent.loadClass(var1, false); } else {/ / if the parent class loader is empty, represents to start the class loader, use the class loader loads var4 = this. FindBootstrapClassOrNull (var1); } } catch (ClassNotFoundException var10) { ; } // If (var4 == null) {long var7 = system.nanotime (); FindClass (var1) = this.findClass(var1); PerfCounter.getParentDelegationTime().addTime(var7 - var5); PerfCounter.getFindClassTime().addElapsedTimeFrom(var7); PerfCounter.getFindClasses().increment(); } } if (var2) { this.resolveClass(var4); } return var4; }}Copy the code
  • protected Class findClass(String name) throws ClassNotFoundException

    • Find the Class with the binary name name and return an instance of the java.lang.Class Class. This is a protected method that the JVM encourages us to override, requiring custom loaders to follow the parent delegate mechanism, This method is called by the loadClass() method after the parent class loader is checked. (The loadClass method is parental delegation and is not recommended to be overridden. This method loads classes using a classpath binary stream.)
    • Prior to JDK 1.2, custom class loading was implemented by inheriting the ClassLoader class and overwriting the loadClass method. In JDK 1.2, however, users are no longer advised to override the loadClass() method. Instead, they are advised to write custom classloading logic in the find Class() method. The findClass() method is called in the loadClass() method, and when the parent of the loadClass() method fails to load, its own findClass() method is called to complete the class load, ensuring that the custom class loader also complies with the parental delegation mechanism
    • Note that the ClassLoader class does not have a concrete code logic that implements the findClass() method. Instead, it throws a ClassNotFoundException, It should also be noted that the findClass() method is usually used in conjunction with the defineClass() method. In general, when customizing a ClassLoader, the findClass() method of the ClassLoader is overridden and the loading rule is written. The bytecode of the class to be loaded is converted into a stream. Then call the defineClass() method to generate the Class object (the underlying findClass is the defineClass method).
  • protected final Class defineClass(String name, byte[] b, int off, int len)

    • The given byte array B is converted to an instance of the Class. The off and len arguments represent the location and length of the actual Class information in the Byte array B, which the ClassLoader obtains externally. This is a protected method that can only be used in custom ClassLoader subclasses

    • The defineClass() method is used to parse the byte stream into a Class object that the JVM can recognize (the logic for this method is implemented in the ClassLoader). This method not only instantiates the Class object from the Class file, Class objects can also be instantiated in other ways, such as by receiving the bytecode of a Class from the network and then converting it to a byte stream to create the corresponding Class object

    • // The relation between findClass and defineClass methods > findClass(String name) throws ClassNotFoundException {byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else {return defineClass(name, classData, 0, classdata.length); }}Copy the code
  • protected final void resolveClass(Class c)

    Link to a Java class specified. Using this method, the Class object of the Class can be created and parsed at the same time. Earlier we said that the linking stage mainly validates the bytecode, allocates memory for class variables and sets initial values, and converts symbolic references in the bytecode file to direct references

  • protected final Class findLoadedClass(String name)

    Find the loaded Class named name and return an example of the java.lang.Class Class. This method is final and cannot be modified

  • private final ClassLoader parent;

    It is also an instance of a ClassLoader, and the ClassLoader represented by this field is also called the parent of the ClassLoader. During class loading, the ClassLoader may delegate some requests to its parents

Loader inheritance

This can be seen according to the loader inheritance diagram

Extension class loaders and system class loaders are both inherited urlClassloaders, -> SecureClassLoader

SecureClassLoader and URLClassLoader

SecureClassLoader extends ClassLoader by adding several methods for using code sources (verification of the location of the source and its certificate) and permission definition Class validation (access to the source code of the Class). It is more associated with its subclass URLClassLoader

As mentioned earlier, ClassLoader is an abstract class and many methods are empty and unimplemented, such as findClass(), findResource(), etc. The URLClassLoader implementation class provides concrete implementations of these methods. The URLClassPath Class has been added to help retrieve Class bytecode streams. When writing a custom class loader, if you don’t have too complicated requirements, you can inherit the URLClassLoader class directly. This way, you can avoid writing the findClass() method and the way to get the bytecode stream, making the custom class loader writing simpler

ExtClassLoader and AppClassLoader

ExtClassLoader does not override the loadClass() method, which is enough to say that it follows the parent delegate pattern. AppClassLoader overrides the loadClass() method, but ultimately calls the parent loadClass() method. So the parental delegation pattern is still followed

Class.forname () with this. LoadClass ()

  • Class.forname () : is a static method, the most commonly used is class.forname (String className); Returns a Class object based on the permission name of the Class passed in. ** This method performs Class initialization while loading the Class file into memory. * * such as: Class. Class.forname (” com. Atguigu. Java. The HelloWorld “);
  • Classloader.loadclass () This is an instance method that requires a ClassLoader object to call. When this method loads the Class file into memory, it does not perform Class initialization (nor does it parse, as you can see from the source code above, just load the Class) until the first time the Class is used. This method requires a ClassLoader object, so you can specify which ClassLoader to use as required, for example: ClassLoader c1 =….. ; c1.loadClass(“com.atguigu.java.HelloWorld”);

Back to the top

4. Parent delegation

Class loaders are used to load classes into the Java virtual machine. Starting with JDK 1.2, classes are loaded using the parent delegate mechanism, which makes the Java platform more secure

Definition:

If a classloader receives a request to load a class, it first does not attempt to load the class itself. Instead, it delegates the request task to the parent classloader, recursively, and returns successfully if the parent classloader can complete the class loading task. Only if the parent class loader is unable to complete the load task, do the load itself

Nature:

The order of class loading is stipulated as follows: boot class loader is loaded first; if not, it is loaded by extension class loader; if not, it is loaded by system class loader or custom class loader

The schematic diagram and flow chart of parental delegation mechanism are as follows:

4.1 Advantages and disadvantages

Advantages of parental delegation:

  • To avoid reloading classes and ensure that a class is globally unique,Java classes have a hierarchy of precedence with their classloaders to avoid reloading classes when the parent has already loaded the class. There’s no need for subclassloaders to load again like if I’m manually loading the same class using different loaders, but because of the parent delegate mechanism, I end up loading the same loader, the same namespace, so it doesn’t load again
  • Protect program security to prevent the core API from being arbitrarily tampered with, such as a custom java.lang.String class, even if the reference to this class, because of the parent delegation mechanism, will be directly started class loader, loaded native String class, protect the core API security

Code support:

Parents delegate mechanism in Java. Lang. This. LoadClass (String, Boolean) in the interface. The logic of this interface is as follows:

  • It looks for the target class in the previous loader’s cache, and returns it if it does
  • Check whether the parent loader of the previous loader is empty. If not, call the parent. LoadClass (name, false) interface for loading
  • Instead, if the parent class loader of the front loader is empty, the findBootstrapClassOrNull(name) interface is called and the boot class loader is loaded
  • If the load fails through the preceding three paths, the findClass(Name) interface is called to load it. This interface eventually calls the native interface of the defineClass series of the java.lang.ClassLoader interface to load the target Java class

The parental delegation model is hidden in steps 2 and 3

For example:

Assuming that the java.lang.Object class is currently loaded, it is clearly a non-core class in the JDK and must therefore be loaded only by the boot classloader. When the JVM is ready to load a java.lang.Object, the JVM defaults to using the system class loader to load it. Since the parent of the slave class loader is the extension class loader, the extension class loader continues to repeat from step 1. Since the class must also not be found in the cache of the extended class loader, step 2 is entered. The parent loader of the extended class is null, so the system calls findClass(String) and eventually loads it through the boot class loader

thinking

If the custom class loader rewritten in Java. Lang. This. LoadClass (String) or Java. Lang. This. LoadClass (String, Boolean) method, Remove the parent delegate mechanism and keep only the first and fourth of the four steps above, so that you can load the core library?

That doesn’t work either! Because the JDK also provides a layer of protection for core libraries. Whether it is a custom class loader, or a system class loader suppression or expansion class loader, Must, in the calling Java. Lang. This. DefineClass (String, byte [], int, int, ProtectionDomain) method, This method, in turn, executes the preDefineClass() interface, which provides protection for the JDK core library

Disadvantages of parental delegation

The delegation process to check whether a class is loaded is one-way. This approach is structurally clear and makes the responsibilities of each ClassLoader very clear, but it also introduces the problem that the top ClassLoader cannot access the classes loaded by the bottom ClassLoader

Typically, the classes in the launcher class loader are the system core classes, including some important system interfaces, and in the application class loader, the application classes. According to this pattern, ** application classes have no problem accessing system classes, but system classes will have problems accessing application classes. ** For example, if you provide an interface in a system class that needs to be implemented in an application class, the interface also binds to a factory method that creates instances of the interface, and both the interface and factory methods are in the startup class loader. In this case, the factory method cannot create an application instance loaded by the application class loader

conclusion

Since the Java Virtual Machine specification does not explicitly require the parent delegate model to be used for the loading mechanism of class loaders, it is simply recommended.

For example, in Tomcat, the loading mechanism adopted by the class loader is somewhat different from the traditional parent delegation model. When the default class loader receives the loading task of a class, it will first load it by itself. When it fails to load, it will delegate the loading task of the class to its superclass loader.

This is also recommended by the Servlet specification

4.2 Break the parent delegation mechanism

The parental delegation model is not a mandatory model, but rather a recommended implementation of class loaders by Java designers to developers

In the Java world, most classloaders follow this model, but there are exceptions. Until the advent of Java modularity, the parent delegate model was “broken” on three major scales

First break of parent delegation: parent delegation itself was not implemented prior to JDK1.2

The first “break” of the parental delegation model actually occurred before the advent of the parental delegation model — in the “ancient” days before JDK 1.2 came out

Since the parent delegate model was introduced after JDK 1.2, but the concept of class loaders and the abstract java.lang.ClassLoader existed in the first version of Java, faced with the existing code for user-defined classloaders, Java designers had to make some compromises when introducing the parental delegation model. To accommodate the existing code, they could no longer technically avoid the possibility of loadClass() being overridden by subclasses. You can only add a new protected method, findClass(), to java.lang.classloader after JDK 1.2 and override it as much as possible when instructing user-written classloading logic instead of writing code in loadClass(). If the parent class fails to load, the parent class automatically calls its own findClass() method to complete the load. This does not prevent users from loading classes as they wish, and ensures that newly written class loaders comply with the parent delegate rules

** Breaks the parent delegate mechanism for the second time: ** Thread context classloader

The second “break” of the parent delegate model is caused by a flaw in the model itself. Parent delegate is a good solution to the problem of consistency of the base type when class loaders cooperate (the more basic classes are loaded by the higher loaders). The base type is called “base”. Because they always exist as apis that are inherited and called by user code, but there are often no perfect rules of programming that are immutable. What if you have an underlying type and need to call user code back?

A good example of this is JNDI services, which are now standard in Java, and whose code is loaded by the bootstrap class loader (added to rt.jar in JDK 1.3), which are certainly very basic types in Java. But JNDI exists for resource lookup and centralized management by invoking code implemented by other vendors and deployed in the application’s ClassPath on the JNDI Service Provider Interface. Now the problem is that there is absolutely no way that the class loader will recognize and load this code when it starts. (SPI: In the Java platform, the interfaces in the core class Rt.jar that provide external services and can be implemented by the application layer themselves are usually called SPI.)

To solve this dilemma, the Java design team introduced a less elegant design: Thread ContextClassLoader. ** This ClassLoader can be set using the setContextClassLoader() method of the java.lang.Thread class. If it has not been set when the Thread is created, It inherits one from the parent thread, which is the application class loader by default if it is not set globally in the application

With thread-context classloaders, programs can do “dirty” things. The JNDI service uses this thread context classloader to load the required SPI service code. This is a kind of behavior that burdens the loader to request the subclass loader to complete the class loading. This behavior actually breaks through the hierarchical structure of the parent delegate model to reverse use the class loader, which has violated the general principle of the parent delegate model, but it is also a helpless thing. Loading in Java that involves SPIs is basically done this way, such as JNDI, JDBC, JCE, JAXB, and JBI. However, when SPI has more than one service provider, the code must be hardcoded according to the type of the specific provider. To eliminate this inelegant method, in JDK 6 the java.util.ServiceLoader class was provided. The configuration information in META-INF/Services, coupled with the chain of responsibility pattern, provides a reasonable solution for SPI loading

Structure:

The default context loader is the application class loader, which acts as a mediator so that the code in the starting class loader can also access the classes in the application class loader

Break the parent delegate mechanism for the third time: modularity

The third “break” of the parental delegation model is due to the user’s desire for program dynamics. For example: Hot Swap code, Hot Deployment module, etc

The key to implementing modular hot deployment in IBM-led JSR-291(OSGI R4.2) is the implementation of its custom class loader mechanism. Each program module (called a Bundle in OSGI) has its own class loader. When a Bundle needs to be replaced, We replace the Bundle with the same loader to achieve hot replacement of the code. In an OSGI environment, class loaders move away from the tree structure recommended by the parent delegate model and evolve into a more complex network structure. When a class load request is received, OSGI performs a class search in the following order:

  • Delegate classes that begin with Java. To the parent class loader
  • Otherwise, delegate the classes in the list to the parent class loader
  • Otherwise, delegate the classes from the Import list to the class loader of the Export class’s Bundle
  • Otherwise, look up the current Bundle’s ClassPath and load it using your own class loader
  • Otherwise, it checks whether the class is in its own Fragment Bundle. If so, it delegates the load to the Fragment Bundle’s classloader
  • Otherwise, look for the Bundle in the Dynamic Import list and delegate the load to the corresponding Bundle’s classloader
  • Otherwise, class lookup fails

Note: Only the first two points still conform to the principles of the parent delegate model; the rest of the class lookup is done in a flat classloader

Summary:

Here, we use the word “broken” to describe the above actions that do not conform to the principles of the parental delegation model, but “broken” is not necessarily derogatory. As long as there is a clear purpose and a good reason, breaking through the old principle is undoubtedly a kind of innovation

As: In the OSGI class loader design does not conform to the traditional architecture of parents delegate class loader, and the industry in order to achieve to its hot deployment and extra high complexity of the dispute is not good, but the respect has to understand basic or technical personnel can reach a consensus, that OSGI in use of class loaders is worth learning, If you understand the OSGI implementation completely, you’ve mastered the essence of a classloader

4.3 Code implementation of hot replacement

Hot replacement refers to the behavior of modifying program programs by replacing program files without stopping the service during program execution. The key requirement for hot replacement is that the service cannot be interrupted and changes must immediately appear on the running system. Basically, most scripting languages, such as PHP, are built to support hot substitution, and once the PHP source file is replaced, the change takes effect immediately without the need to restart the Web server

But hot replacement is not inherently supported in Java. If a class is already loaded into the system, you can’t make the system load and redefine the class by modifying the class file.

Therefore, a feasible way to implement this functionality in Java is to use ClassLoader flexibly

** Note: Classes with the same name loaded by different classloaders belong to different types and cannot be converted and compatible with each other. That is, two different classloaders that load the same class are considered completely different ** inside the virtual machine

According to this feature, it can be used to simulate the implementation of hot replacement. The basic idea is shown in the figure below:

Code implementation:

Compile the following class as a class file using Javac

Public void hot() {system.out.println ("OldDemo1"); public void hot() {system.out.println ("OldDemo1"); }}Copy the code

Custom ClassLoader, no need to worry too much

public class MyClassLoader extends ClassLoader { private String rootDir; public MyClassLoader(String rootDir) { this.rootDir = rootDir; } protected Class<? > findClass(String className) throws ClassNotFoundException { Class clazz = this.findLoadedClass(className); FileChannel fileChannel = null; WritableByteChannel outChannel = null; if (null == clazz) { try { String classFile = getClassFile(className); FileInputStream fis = new FileInputStream(classFile); fileChannel = fis.getChannel(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); outChannel = Channels.newChannel(baos); ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (true) { int i = fileChannel.read(buffer); if (i == 0 || i == -1) { break; } buffer.flip(); outChannel.write(buffer); buffer.clear(); } byte[] bytes = baos.toByteArray(); clazz = defineClass(className, bytes, 0, bytes.length); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileChannel ! = null) fileChannel.close(); } catch (IOException e) { e.printStackTrace(); } try { if (outChannel ! = null) outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } return clazz; } private String getClassFile(String className) {return rootDir + "\\" + classname.replace ('.', '\\') + ".class"; }}Copy the code

Use custom class loading to load classes and use

Public class LoopRun {public static void main(String args[]) {// Call Demo1 while (true) {try {//1. MyClassLoader loader = new; MyClassLoader = new; MyClassLoader = new MyClassLoader("D:\\code\\workspace_idea5\\JVMDemo1\\chapter04\\src\\"); Clazz = loader.findClass(" com.atguigu.java1.demo1 "); //3. Create an instance of the runtime class Object Demo = clazz.newinstance (); M = clazz.getMethod("hot"); //5. Call the specified method m.invoke(demo); Thread.sleep(5000); } catch (Exception e) { System.out.println("not find"); try { Thread.sleep(5000); } catch (InterruptedException ex) { ex.printStackTrace(); } } } } }Copy the code

Modify the Demo1 class method at run time and recompile to print the result :OldDemo1

OldDemo1
OldDemo1
OldDemo1---> NewDemo1
OldDemo1---> NewDemo1
OldDemo1---> NewDemo1
OldDemo1---> NewDemo1
Copy the code

Back to the top

5. Sandbox safety mechanism

Function:

  • Protect program security
  • Protect Java native JDK code

The core of the Java security model is the Java Sandbox. What is a Sandbox? The sandbox is an environment that restricts the execution of programs

The sandbox mechanism restricts Java code to a virtual machine (JVM) -specific run scope and strictly limits code access to local system resources. ** This ensures limited isolation of code to prevent damage to the local system

Sandboxes mainly restrict access to system resources. What do system resources include? CPU, memory, file system, network. Different levels of sandboxes can limit access to these resources differently. All Java programs can be run with sandboxes, and security policies can be customized

  1. JDK 1.0 times

    In Java, executables are divided into local code and remote code, with local code being trusted by default and remote code being untrusted. For trusted native code, you have access to all local resources. For untrusted remote code, security in early Java implementations relied on a Sandbox mechanism. The JDK 1.0 security model is shown below

  1. JDK 1.1 times

    Such strict security in JDK 1.0 also made it difficult for programs to extend their capabilities, such as when users wanted remote code to access files on the local system

    In subsequent JDK 1.1 releases, security policies were added to improve the security mechanism. Allows users to specify code access to local resources

    The JDK 1.1 security model is shown below

  1. JDK 1.2 times

    In JDK 1.2, security was improved again and code signing was added. Regardless of the local code or remote code, according to the user’s security policy Settings, the class loader will be loaded into the VIRTUAL machine with different permissions to achieve differentiated code execution permission control. The JDK 1.2 security model is shown below:

  2. JDK 1.6 times

    The latest security mechanism implementation, then introduced the Domain (Domain) concept

    The virtual machine loads all the code into different system domains and application domains. The system domain section is specifically responsible for interacting with key resources, while the application domain section accesses various needed resources through the system domain’s partial proxy. Different Protected domains in VMS have different permissions. Class files that exist in different domains have full permissions for the current domain, as shown below. The latest security model (JDK 1.6)

Back to the top

6. Custom class loaders

Why custom class loaders?

  • Isolation load class

    In some frameworks, middleware is isolated from application modules and classes are loaded into different environments. For example, a container framework in Ali uses a custom class loader to ensure that jar packages dependent on the application do not affect the jar packages used by the middleware runtime. Another example is Tomcat, a Web application server, which has several kinds of internal loaders to isolate different applications on the same Web application server. (Class arbitration –> Class conflict)

  • Modify the way classes are loaded

    The loading model of the class is not mandatory. Except for Bootstrap, other loading is not mandatory, or dynamic loading can be carried out at a certain point in time according to the actual situation

  • Extended load source

Such as loading from a database, the web, or even a TV set-top box

  • Prevent source code leakage

Java code is easy to compile and tamper with and can be compiled and encrypted. Then class loading also needs to be customized, restoring the encrypted bytecode

Common Scenarios

Implementing similar in-process isolation, class loaders are actually used as different namespaces to provide container-like, modular effects. For example, two modules that depend on different versions of a library may not interfere with each other if they are loaded by different containers. This aspect is epitomized by Java EE and OSGI, JPMS and other frameworks

Applications need to obtain class definition information from different data sources, such as network data sources, rather than native document systems. Or you need to manipulate the bytecode yourself, dynamically modify or generate the type

Note:

In general, using different class loaders to load different function modules can improve the security of application programs. However, loaders are prone to bad things when Java type conversions are involved. A Java type conversion can only be performed if both types are loaded by the same loader. Otherwise, an exception will occur during the conversion

Code implementation

By customizing your own class loaders, you can redefine the loading rules of your classes to implement some custom processing logic

implementation

  • Java provides the abstract java.lang.ClassLoader class, and all user-defined classloaders should inherit the ClassLoader class (or its subclasses).
  • When customizing a ClassLoader subclass, there are two common approaches:
    • Method 1: Override the loadClass() method
    • Method 2: Rewrite the findClass() method (recommended)

Contrast:

The two methods are essentially the same, after all loadClass() also calls findClass(), but logically it is best not to modify the internal logic of loadClass() directly. The recommended approach is to simply override the loading method of a custom Class in findClass(), specifying the Class name as an argument and returning a reference to the corresponding Class object

  • The loadClass() method is where the parental delegate model logic is implemented, and tampering with this method can cause problems by breaking the model. Therefore, it is best to make small changes within the framework of the parental delegation model without breaking the existing stable structure. It also avoids having to write duplicate parent delegate code when overwriting the loadClass() method. In terms of code reusability, it is always better not to modify this method directly
  • When a custom class loader is written, the loadClass() method can be called in your program to implement class loading operations

Description:

  • The parent class loader is the system class loader
  • All class loading in the JVM will use Java. Lang. This. The loadClass (String) interface (custom class loaders and rewrite the Java. Lang. This. Except the loadClass interface), Even the core JDK libraries are no exception

Code implementation:

Public class MyClassLoader extends ClassLoader {private String byteCodePath; public MyClassLoader(String byteCodePath) { this.byteCodePath = byteCodePath; } public MyClassLoader(ClassLoader parent, String byteCodePath) { super(parent); this.byteCodePath = byteCodePath; } @Override protected Class<? > findClass(String className) throws ClassNotFoundException { BufferedInputStream bis = null; ByteArrayOutputStream baos = null; String fileName = byteCodePath + className + ". Class "; String fileName = byteCodePath + className + ". Bis = new BufferedInputStream(new FileInputStream(fileName)); Baos = new ByteArrayOutputStream(); // The process of reading and writing out the data int len; byte[] data = new byte[1024]; while ((len = bis.read(data)) ! = -1) { baos.write(data, 0, len); Byte [] byteCodes = baos.tobytearray (); // Call defineClass() to convert the byte array data into an instance of Class. Class clazz = defineClass(null, byteCodes, 0, byteCodes.length); return clazz; } catch (IOException e) { e.printStackTrace(); } finally { try { if (baos ! = null) baos.close(); } catch (IOException e) { e.printStackTrace(); } try { if (bis ! = null) bis.close(); } catch (IOException e) { e.printStackTrace(); } } return null; }}Copy the code

Use:

public class MyClassLoaderTest { public static void main(String[] args) { MyClassLoader loader = new MyClassLoader (" D: \ \ dev \ \ workspace \ \ demo \ \ code \ \ JVMDemo1 \ \ chapter04 \ \ SRC \ \ com \ \ atguigu \ \ java1 \ \ "); try { Class clazz = loader.loadClass("Demo1"); Println (" + clazz.getClassLoader().getClass().getName()); Println (" + clazz.getClassLoader().getparent ().getClass().getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); }}}Copy the code

Print result:

Class loader for loading such: com. Atguigu. Java2. MyClassLoader / / used successfully loaded current not the class of the parent class loader loader as follows: Sun.misc.Launcher$AppClassLoader // The default parent class of the custom loader is the system loaderCopy the code

Back to the top

7. New features in JDK9

To ensure compatibility, JDK 9 does not fundamentally change the three-tier classloader architecture or the parent delegate schema, but there are still some notable changes to make modular systems run smoothly

  1. The extension mechanism was removed, and the extension classloader was retained for backward compatibility reasons, but renamed the Platform Class Loader. This can be obtained via the new ClassLoader method getPlatformClassLoader()

    When JDK 9 was built on modularity (the original Rt.jar and tools.jar were split into dozens of JMOD files), the Java class library was already naturally extensible, so there was no need to keep the \lib\ext directory. The previous mechanism of using this directory or the java.ext.dirs system variable to extend the functionality of the JDK no longer has merit

  2. Neither platform class loaders nor application class loaders inherit from java.net.URLClassLoader

    Now start the class loader, platform class loaders, all application class loader inheritance in JDK. Internal. Loader. BuiltinClassLoader

    If a program relies directly on this inheritance, or on the specific methods of the URLClassLoader class, the code will most likely crash in JDK 9 and later

  3. In Java 9, class loaders have names. The name is specified in the constructor and can be obtained using the getName() method. The name of the Platform class loader is Platform, and the name of the application class loader is App. Class loader names can be useful when debugging classloader-related problems

  4. The bootloader is now implemented within the JVM in collaboration with the Java class library (formerly C++), but for compatibility with previous code, null is still returned in scenarios where the bootloader is fetched, not the BootClassLoader instance

  5. The delegation relationship for class loading has also changed

    When platform and application class loaders receive class loading requests, they should first determine whether the class can belong to a certain system module before assigning it to the parent loader. If such attributing relationship can be found, they should preferentially assign it to the loader responsible for which module to complete the loading

Additional Information:

In the Java modular system, three class loaders are specified to be responsible for each module loaded:

  • Starts the module that the class loader is responsible for loading

    java.base    		java.security.sasl
    java.datatransfer 	java.xml
    java.desktop 		jdk.httpserver
    java.instrument 	jdk.internal.vm.ci
    java.logging 		jdk.management
    java.management 	jdk.management.agent
    java.management.rmi jdk.naming.rmi
    java.naming 		jdk.net
    java.prefs 			jdk.sctp
    java.rmi 			jdk.unsupported
    Copy the code
  • The platform class loader is responsible for loading modules

    java.activation* 	jdk.accessibility
    java.compiler* 		jdk.charsets
    java.corba* 		jdk.crypto.cryptoki
    java.scripting 		jdk.crypto.ec
    java.se 			jdk.dynalink
    java.se.se 			jdk.incubator.httpclient
    java.security.jgss 	jdk.internal.vm.compiler*
    java.smartcardio 	jdk.jsobject
    java.sql 			jdk.localedata
    java.sql.rowset 	jdk.naming.dns
    java.transaction* 	jdk.scripting.nashorn
    java.xml.bind* 		jdk.security.auth
    java.xml.crypto 	jdk.security.jgss
    java.xml.ws* 		jdk.xml.dom
    java.xml.ws.annotation* jdk.zipfs
    Copy the code
  • The application class loader is responsible for loading modules

    jdk.aot 			jdk.jdeps
    jdk.attach 			jdk.jdi
    jdk.compiler 		jdk.jdwp.agent
    jdk.editpad 		jdk.jlink
    jdk.hotspot.agent 	jdk.jshell
    jdk.internal.ed 	jdk.jstatd
    Copy the code