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 runs is up to the Execution Engine.

Classloaders first appeared in Java version 1.0, when they were developed solely for Java applets. But today class loaders are shining in 0SGi, 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 loading classification: explicit 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, such as directly loading class objects using class.forname (name) or this.getClass ().getClassLoader ().loadClass ().
  • Implicit loading means that class objects are not directly loaded by calling the ClassLoader method in the code, but are automatically loaded into the memory by the VM. For example, when the class file of a certain class is loaded, the class file of that class references the object of another class. In this case, the extra referenced classes are automatically loaded into the memory by the JVM.

In daily development, these two methods are usually mixed.

In daily development, these two methods are often mixed.

case

public class UserTest { public static void main(String[] args) { User user = new User(); / / implicit load try {Class clazz = Class. Class.forname (" com. DSH. Jvmp2. Chapter04. Java. The User "); / / explicit loading this getSystemClassLoader () loadClass (" com. DSH. Jvmp2. Chapter04. Java. The User "); } catch (ClassNotFoundException e) {e.printstackTrace (); }}}Copy the code

The necessity of class loading

In general, Java developers do not need to use class loaders explicitly in their programs, but it is important to understand the loading mechanism of class loaders. From the following aspects:

  • Avoid in development in Java. Lang. ClassNotFoundException exception or Java. Lang. A NoClassDefFoundError, unprepared. 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

1. 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.

2. Namespace

  • Each class loader has its own namespace, which is made up of classes loaded by that loader and all of its parents
  • 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.

public static void main(String[] args) { String rootDir = "/Users/dongshuhuan/JavaProjects/JVM_study/src"; Try {// create custom class loader1 UserClassLoader loader1 = new UserClassLoader(rootDir); Class clazz1 = loader1.findClass("com.dsh.jvmp2.chapter04.java.User"); UserClassLoader loader2 = new UserClassLoader(rootDir); Class clazz2 = loader2.findClass("com.dsh.jvmp2.chapter04.java.User"); System.out.println(clazz1 == clazz2); //false clazz1 and clazz2 correspond to different class template structures. System.out.println(clazz1.getClassLoader()); //com.dsh.jvmp2.chapter04.java.UserClassLoader@1d44bcfa System.out.println(clazz2.getClassLoader()); //com.dsh.jvmp2.chapter04.java.UserClassLoader@6f94fa3e //###################### Class clazz3 = ClassLoader.getSystemClassLoader().loadClass("com.dsh.jvmp2.chapter04.java.User"); System.out.println(clazz3.getClassLoader()); //sun.misc.Launcher$AppClassLoader@18b4aac2 // The parent class of the custom class loader is the system class loader System.out.println(clazz1.getClassLoader().getParent()); //sun.misc.Launcher$AppClassLoader@18b4aac2 } 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, such as the inside of the JDK ServiceProvider/ServiceLoader mechanism, the user can on standard API framework, to provide their own implementation,

The JDK also needs to provide some default reference implementation. For example, INDI, JDBC, file systems, Ciphers, and many other aspects of Java make use of this mechanism, which is not loaded using the parent 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, without the necessary isolation, there is no way to implement the container logic using the classloader.
  • 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.

Class loader classification

The JVM supports two types of classloaders, Bootstrap classloaders and User-defined classloaders.

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, as shown in the code
class ClassLoader{ ClassLoader parent; Public ChildClassLoader(ClassLoader parent){//parent = new ParentClassLoader() this.parent = parent; } } class ParentClassLoader extends ClassLoader{ public ParentClassLoader(ClassLoader parent){ super(parent) } } class ChildClassLoader{ public ChildClassLoader(ClassLoader parent){//parent = new ParentClassLoader(); super(parent); }}Copy the code

Bootstrap ClassLoader

Start the ClassLoader (Bootstrap ClassLoader)

  • This class loading is implemented in C/C++ and is nested within the JVM.
  • It loads the Java core library (JAVA_HOME/jre/lib/rt. Jarbsun. Boot. The class. The path 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 to load only classes whose package names start with Java, Javax, and Sun
  • Loads extension classes and application classloaders and specifies them as parent classloaders.

Using the -xx :+TraceClassLoading argument

Start class loader written in C++? Yes!

  • C/C++: pointer function & function pointer, C++ support multiple inheritance, more efficient
  • Java: evolved from C++, (C++) — version, single inheritance

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.

Ex. :

public class ClassLoaderTest { public static void main(String[] args) { System. The out. Println (" * * * * * * * * * * to start the class loader * * * * * * * * * * * * * * "); / / get BootstrapClassLoader can load the path of the API URL [] urLs = sun. Misc. The Launcher. GetBootstrapClassPath (.) getURLs (); for (URL element : urLs) { System.out.println(element.toExternalForm()); } / / from the above path choice of a class, let's see what his class loader is: the bootstrap class loader this this = Java security. The Provider. The class. The getClassLoader (); System.out.println(classLoader); / / null the bootstrap class loader is to get less than System. Out. The println (" * * * * * * * * * * * the extension class loader * * * * * * * * * * * * * "); String extDirs = System.getProperty("java.ext.dirs"); for (String path : extDirs.split(";" )) { System.out.println(path); } / / / / / / from the above path choice of a class, let's see what's his class loader: extension class loader this classLoader1 = sun. Security. Ec. CurveDB. Class. GetClassLoader (); System.out.println(classLoader1); //sun.misc.Launcher$ExtClassLoader@1540e19d } }Copy the code

The output

********** Start the classloader ************** File: / Library/Java/JavaVirtualMachines jdk1.8.0 _171. JDK/Contents/Home/jre/lib/resources. The jar File: / Library/Java/JavaVirtualMachines jdk1.8.0 _171. JDK/Contents/Home/jre/lib/rt. The jar File: / Library/Java/JavaVirtualMachines jdk1.8.0 _171. JDK/Contents/Home/jre/lib/sunrsasign jar File: / Library/Java/JavaVirtualMachines jdk1.8.0 _171. JDK/Contents/Home/jre/lib/jsse jar File: / Library/Java/JavaVirtualMachines jdk1.8.0 _171. JDK/Contents/Home/jre/lib/jce jar File: / Library/Java/JavaVirtualMachines jdk1.8.0 _171. JDK/Contents/Home/jre/lib/charsets. The jar File: / Library/Java/JavaVirtualMachines jdk1.8.0 _171. JDK/Contents/Home/jre/lib/JFR jar File: / Library/Java/JavaVirtualMachines jdk1.8.0 _171. JDK/Contents/Home/jre/classes null * * * * * * * * * * * the extension class loader * * * * * * * * * * * * * / Users/dongshuhuan/Library/Java/Extensions: / Library/Java/JavaVirtualMachines jdk1.8.0 _171. JDK/Contents/Home/jre/lib/ext: /Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java sun.misc.Launcher$ExtClassLoader@4b1210eeCopy the code

System Class loader (AppClassLoader)

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

User-defined class loaders

  • In] AVA’s daily 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.
  • One of the key elements of the vitality and charm of the Java language is that Java developers can customize class loaders to dynamically load class libraries

The loading source can be a local JAR package or a remote resource on the network.

  • There are plenty of examples of how plug-ins can be implemented through classloaders. For example, the well-known SGI component framework, as well as

Eclipse’s plug-in mechanism. Class loaders provide a mechanism for dynamically adding new functionality to an application without having to repackage and distribute the application.

  • At the same time, custom loaders can achieve application isolation, such as Tomcat, Spring and other middleware and component frameworks have internal implementation of custom loading

And isolate different component modules through a custom loader. This mechanism is so much better than C/C++ programs that it is almost impossible to add new features without modifying C/C++ programs, and a single compatibility prevents all good ideas.

  • Custom classloaders usually need to be inherited from classloaders.

Test different class loaders

Each Class object contains a reference to the ClassLoader that defines it.

How to obtain a classLoader

way
Get the current ClassLoader -> clazz.getClassLoader()
Thread.currentthread ().getContextClassLoader()
Get this system – > this. GetSystemClassLoader ()

Note: from the point of view of the program, the boot class loader and the other two types of loader (system class loader and extension class loader) are not the same level of the loader, the boot class loader is written in C++ language, and the other two types of loader is written in Java language. Since the boot classloader is not a Java class at all, only null values can be printed in a Java program.

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. Class loaders for array classes are returned by class.getClassloader (), the same as the Class loaders for the element types in the array. If the element type in the array is a primitive data type, the array class has no classloader.

Ex. :

Public class ClassLoaderTest1 {public static void main(String[] args) {// get the systemClassLoader ClassLoader.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); //null //########################### try { ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();  System.out.println(classLoader); //null // Custom classes use the system ClassLoader classLoader1 = by default Class.forName("com.dsh.jvmp2.chapter04.java.ClassLoaderTest1").getClassLoader(); System.out.println(classLoader1); //sun.misc.Launcher$AppClassLoader@18b4aac2 // About the loading of array types: the class used has the same loader as the class of array elements. String[] 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()); //null: Basic data types do not require class loaders (predefined by the VM) system.out.println (thread.currentThread ().getContextClassLoader()); //sun.misc.Launcher$AppClassLoader@18b4aac2 } catch (ClassNotFoundException e) { e.printStackTrace(); }}}Copy the code

ClassLoader source code parsing

The relationship between a ClassLoader and an existing ClassLoaderIn 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 superclass loader for the class loader
  • public Class<? > loadclass(String name) throws ClassNotFoundExceptionLoad a Class named name that returns an instance of the java.lang.Class Class. If the class is not found, a classNotFoundException exception is returned. The logic in this method is the implementation of the parent delegate pattern.
  • protected Class<? > FindClass (String name) throws ClassNotFoundExceptionFind the Class with the binary name name and return an instance of the java.1ang.Class Class. This is a protected method that the JVM encourages us to override, requiring the custom loader to follow the parent delegate mechanism, which is called by the loadClass () method after checking the parent class loader.

    Prior to JDK1.2, custom class loading was implemented by inheriting the ClassLoader class and overwriting the loadClass method. However, after IDK1.2, users are no longer advised to override the loadClass () method, instead writing custom classloading logic in

In the findClass () method, we can see from the previous analysis that the findClass () method is called in the loadClass () method, when the parent loadClass () method load failure, will call its own findClass () method to complete the class loading. This ensures that custom class loaders also conform to the parent delegate pattern. Note that there is no specific code logic in the ClassLoader class 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. Typically, when customizing a ClassLoader, you override the findClass () method of the ClassLoader directly, write a loading rule, get the bytecode of the Class to be loaded, convert it into a stream, and then call defineClass () to generate the Class object of the Class. `

  • 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 already implemented in the ClassLoader). This method can be used to instantiate Class objects not only from Class files, but also in other ways. For example, the bytecode of a Class is received over the network and then converted into a byte stream to create the corresponding Class object. The defineClass () method is usually used together with the findClass () method. In general, when a custom ClassLoader is defined, the findClass () method of the ClassLoader is overridden directly and the loading rules are written. The bytecode of the class to be loaded is obtained and converted into a stream. Then call defineClass() to generate the Class object of the Class – a simple example: protected Class
FindClass (String name) throws ClassNotFoundException{byte[] classData = getclassData (name); If (classData == null) {throw new ClassNotFoundException (); } else {return defineClass (name, classData, 0, classdata.length); }

  • 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 phase is mainly about verifying the bytecode, allocating memory and setting initial values for class variables and converting symbolic references in the bytecode file to direct references.
  • protected final Class<? > findLoadedClass(String name)Find the loaded Class named name and return an instance 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.

LoadClass method resolution

This test code. GetSystemClassLoader (.) loadClass (” com. DSH. Jvmp2. Chapter04. Java. The User “);

protected Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) { Is the same Class< already loaded in the cache? > c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); Try {// Get the parent of the current class loader if (parent! C = parent.loadClass(name, false); } else {// The parent class loader is the boot class loader c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent Class loader} if (c == null) {if (c == null) {if (c == null) {if (c == null) {if (c == null) {if (c == null) {if (c == null) {if (c == null) {if (c == null) System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); ResolveClass (c); resolveClass(c); } return c; }}Copy the code

SecureClassLoader and URLClassLoader

SecureClassLoader then extends the ClassLoader to include several methods for using code sources (verifying the location of the source and its certificate) and permission definition class validation (referring to access to the source code of the class), which we don’t normally deal with directly. 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

After learning about URLClassLoader, we can look at the remaining two class loaders, namely the extension class loader ExtClassLoader and the system class loader AppClassLoader, which both inherit from URLClassLoader. Is a static inner class for sun.misc.Launcher. Sun.misc.Launcher is mainly used by the system to start the main application. ExtClassLoader and AppClassLoader are created by sun.misc.Launcher.

We find that ExtClassLoader does not override the 1oadClass () method, which is sufficient to indicate that it follows the parent delegate mode. AppClassLoader overloads the loadClass () method, but ultimately calls the parent 1oadClass () method. So the parental delegation pattern is still adhered to.

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 fully qualified 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 until the first time the Class is used. This method, because it needs to get a ClassLoader object, can specify which ClassLoader to use as needed. Such as: this cl =… ;

Cl. LoadClass (” com. Atguigu. Java. The HelloWorld “);

Parental delegation model

Class loaders are used to load classes into the Java virtual machine. Since IDK1.2, classes are loaded using the parent delegate mechanism, which can better ensure the security of the Java platform.

Definition and Essence

Definition 1.

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 of 2.

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.


Advantages of parent delegation

1. Advantages of parental delegation mechanism

  • Avoid reloading classes to 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, so there is no need for the child ClassLoader to load the class again.

  • Protect program security and prevent the core API from being tampered with

2. Code to support Parents delegate mechanism in Java. Lang. ClassLoadelr. LoadClass (String, Boolean) in the interface. The logic of this interface is as follows:

  • (1) First look for the target class in the cache of the current loader, if there is, directly return.
  • (2) Check whether the parent loader of the current loader is empty. If not, call the parent. LoadClass (name, false) interface to load.
  • (3) Otherwise, if the parent class loader of the current loader is empty, the findBootstrapClassOrNull (name) interface is called and the boot class loader is loaded.
  • (4) If the load fails through the above three paths, the findClass (name) interface is called to load. The interface will eventually be called

The native interface of the defineClass series of the java.lang.ClassLoader interface loads the target Java class. The parental delegation model is hidden in steps 2 and 3.

3. For example, suppose that the java.lang.Object class is currently loaded. Obviously, this class belongs to one of the core classes in IDK, so it must be loaded only by the boot class loader. When the JVM is ready to load Javajang.0bject, by default the JVM uses the system class loader to load it. Following the logic of the previous four steps, the class cannot be found in the system class cache at step 1, so step 2 is used. Since the parent of the class loader from the system is the extension class loader

4. Think about 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 steps 1 and 4, and then load the core library?

That doesn’t work either! Because the JDK also provides a layer of protection for core libraries. Custom class loaders, and system class loader or extension class loader, finally all must call Java lang. This. The defineClass (String, byte [], int, int, ProtectionDomain) method, This method, in turn, executes the preDefineClass () interface, which provides protection for the JDK core library.

The delegate process of checking whether a class is loaded is one-way. Although this method is clear in structure and makes the responsibilities of each ClassLoader very clear, it also brings a problem, that is, 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, the application class has no problem accessing the system class, but the system class will have problems accessing the application class. For example, if you provide an interface in a system class that needs to be implemented in an application class, the interface is bound 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.

6. Conclusion: Since the Java Virtual Machine specification does not explicitly require the parent delegate model to be used for class loaders, it simply recommends it. 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.

Break the parent delegate mechanism

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

Most classloaders in the Java world follow this model, but there are some exceptions, and until the advent of Java modularity, the parent delegate model has been “broken” on three major scales.

Breaking parental delegation for the first time: The first “breaking” of the parent delegation model actually happened before the existence of the parent delegation model — the “ancient” days before JDK1.2 came out. Since the parent delegate model was introduced after JDK1.2, but the concept of class loaders and the abstract java.lang.ClassLoader existed in the first version of Java, the code for user-defined class loaders already existed. 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 IDK1.2 and override it as much as possible when instructing user-written classloading logic rather than writing code in loadClass (). If the parent fails to load the class, the user will automatically call its own findClass () method. If the parent fails to load the class, the user will automatically call its own findClass () method to load the class. It also ensures that newly written classloaders comply with the parent delegate rules.

The second damage parents delegate mechanism: thread context class loader Parents delegate second “destruction” of the model is determined by defects in the model itself, parents delegate well solved the various class loader collaborate basis types of consistency issues (the base class by the upper level of the loader to load), the base type is called the “foundation”, 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 that calls back to the user’s code?

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 JDK1.3), and are certainly of a very basic type in Java. But THE purpose of JNDI is to find and centrally manage resources. It requires invoking INDI Service Provider Interface (SPI) code implemented by other vendors and deployed in an application’s ClassPath. Here’s the problem. There is no way that the bootstrap class loader will recognize and load this code, so what happens? (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: the 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 will inherit one from the parent Thread, if it has not been set at the global level of the application. This class loader is the application class loader by default.

With thread-context classloaders, programs can do “dirty” things. JNDI service USES the thread context class loader to load the required SPI service code, this is a parent class loader to request a subclass loader finish class loading behavior, this behavior is, in fact, get through the parents delegation model hierarchy to reverse the use of class loaders, has violated the parents delegation model of general principles of but also helpless. Loading in Java that involves SPIs, such as JNDI, JDBC, JCE, JAXB, and JBI, is basically done this way. 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 implementation, in IDK 6, the JDK provided java.util.Serviceloader classes. The configuration information in META-INF/ Services, coupled with the chain of responsibility pattern, provides a reasonable solution for SPI loading.

The default context loader is the application class loader, so that the context loader acts as a mediator, making the classes in the application class loader accessible to code in the startup class loader.

The third break of the parent delegate mechanism: The third “break” of the parent delegate model is due to the user’s pursuit of program dynamics. For example: Hot Swap code, Hot Deployment module, etc

The key to implementing modular hot deployment in IBM-led JSR 291 (OSGiR4.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 the 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 classload request is received, OSGi performs a class search in the following order:

  • 1) Delegate classes starting with Java.* to the parent class loader.
  • 2) Otherwise, delegate the classes in the list to the parent class loader.
  • 3) Otherwise, delegate the classes from the Import list to the class loader of the Export class Bundle.
  • 4) Otherwise, find the current Bundle’s ClassPath and load it using your own class loader.
  • 5) Otherwise, check whether the class is in the Fragment Bundle. If so, delegate the load to the class loader of the Fragment Bundle.
  • 6) Otherwise, find the Bundle from the Dynamic Import list and delegate the load to the corresponding Bundle’s class loader.
  • 7) 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 behavior that does 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: the design of the OSGi class loader is not in conformity with 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 for the technical personnel have to understand the basic or can reach a consensus, on the use of class loaders that OSGi is worth learning, Fully understand the implementation of 0SGi, even if you know the essence of class loaders.

Implementation of hot substitution

Hot replacement refers to the modification of program behavior by replacing program files without stopping the service while the program is running. 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 replacement. Once you replace the PHP source file, the change takes effect immediately, without restarting the Web server.

But hot replacement is not naturally 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 load the same class. Inside the virtual machine, the two classes are considered completely different.

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

The execution of the above code program is output every 5 seconds

  • First define the output method in Demo1 and compile it to class using Javac
  • Run the program, outputOldDemo1
  • Modify the output method in Demo1 and compile again to a class file using javac, at which point the class file is replaced
  • Look at the program output. The program outputOldDemo1---> NewDemo1

Demo1

public class Demo1 { public void hot() { // System.out.println("OldDemo1"); System.out.println("OldDemo1-- > NewDemo1"); }}Copy the code

Custom class loader MyClassLoader

/** * 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

Test code LoopRun

public class LoopRun { public static void main(String args[]) { while (true) { try { //1. Create a custom class loader instance of String rootDir = "/ Users/dongshuhuan/JavaProjects/JVM_study/SRC"; MyClassLoader loader = new MyClassLoader(rootDir); / / (2) of the specified Class clazz = loader. The findClass (" com. DSH. Jvmp2. Chapter04. Java1. Not "); //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

Sandbox security mechanism

Sandbox security mechanism

  • Ensure program security
  • Protect Java native JDK code

At the heart 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 specific running scope of the virtual machine (JVM) and severely restricts code access to local system resources. This ensures limited isolation of the code and prevents 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 program runs can specify the sandbox, can customize the security policy.

1. In JDK1.0, executables in Java were divided into local code and remote code. Local code was treated as trusted by default, while remote code was treated as 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 following figure shows the IDK1.0 security model

The strict security mechanism in JDK1.0 also makes it difficult to extend the functionality of programs, for example, when users want remote code to access files on the local system.

Therefore, in the subsequent Java1.1 release, the security mechanism was improved and security policies were added. Allows users to specify code access to local resources. The JDK1.1 security model is shown below

In Java1.2, the security mechanism 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 JDK1.2 security model is shown below:

4. The latest security mechanism implementation in JDK1.6 period introduces the concept of Domain.

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 in the figure below. The latest security model (JDK1.6)

A loader for custom classes

1. 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 the Tomcat Web application server, which internally defines several types of 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

  • Preventing 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.

2. Common scenarios

  • Implementing similar in-process isolation, class loaders are actually used as different namespaces to provide a container-like, modular effect. For example, two modules that depend on different versions of a library may not interfere with each other if they are loaded separately by different containers. This aspect is epitomized by Java EE and OSGI, JPMS and other frameworks.
  • Applications need to get class definition information from a different data source, such as a network data source, rather than the local file system. Or you need to manipulate the bytecode yourself, dynamically modify or generate the type.

3. Note: In general, using different class loaders to load different function modules improves application security. 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.

implementation

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

1. Implementation method

  • Java provides the abstract java.lang.ClassLoader class, which should be inherited by all user-defined classloaders.
  • When customizing a ClassLoader subclass, there are two common approaches:
    • Method 1: Override the loadClass () method
    • Method 2: Override the findClass () method

LoadClass () also calls findClass (), but it is logically 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. At the same time, you avoid rewriting yourself

The loadClass () method must write duplicate code for the parent delegate, and it is always better not to modify this method directly in terms of code reusability.

  • When a custom class loader is written, the loadClass () method can be called in your program to implement class loading operations.

3. The instructions

  • 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. The loadClass (String) except the interface), Even DDK’s core class library is no exception.

code

Custom class loaders

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

The test code

public class MyClassLoaderTest { public static void main(String[] args) { MyClassLoader loader = new MyClassLoader("/Users/dongshuhuan/JavaProjects/JVM_study/src/com/dsh/jvmp2/chapter04/java1/"); try { Class clazz = loader.loadClass("Demo1"); Println (" + clazz.getClassLoader().getClass().getName()); / / com. DSH. Jvmp2. Chapter04. Java2. MyClassLoader System. Out.println (" load current not the class of the parent class loader loader as follows: " + clazz.getClassLoader().getParent().getClass().getName()); //sun.misc.Launcher$AppClassLoader } catch (ClassNotFoundException e) { e.printStackTrace(); }}}Copy the code

New features in Java9

To ensure compatibility, JDK9 does not fundamentally change the three-tier classloader architecture or the parent delegate model, but there are still some notable changes to make the modular system run smoothly.

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

Jar and tools.jar are split into dozens of JMOD files. The Java class libraries in JDK9 are naturally extensible, so there is no need to keep <JAVA_HOME>\lib\ext. 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.

  1. 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 specific methods of the URLClassLoader class, the code is likely to crash in JDK9 and later.

  1. 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.
  2. 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.
  3. 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 be assigned to a 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 that module to complete the loading.

Schematic diagram of the parent delegate pattern