Summary of Java class loaders

  • Date:2015-5-10
  • Tag:java;ClassLoader;ClassLoader;ExtClassLoader;AppClassLoader
  • Author:Snow
  • Email:[email protected]

1. What (what?)

1. The Java class loader is a part of the Java runtime environment and is responsible for dynamically loading Java classes into the memory space of the JVM. Every Java class must be loaded into memory by some class loader. Every class loader has a parent class loader (BootStrap bootstrap class loader does not).

2. There are three default loaders in the JVM:
(1) BootStrap: Bootstrap class loader. This loader is very special. It is not a JAVA class, so it does not need to be loaded by others. It is nested in the JVM kernel, which means that BootStrap is started when the JVM starts. It is a binary code written in C++ and can be loaded by other people. the type. This is also the reason why System.class.getClassLoader() results in null, since it is not a JAVA class, its reference returns null. Responsible for loading the core Java library, stored in /jre/lib/rt.jar
(2) ExtClassLoader: Extended class loader.
(3) AppClassLoader: load java classes according to the class path. Generally, our custom classes are loaded through this AppClassLoader.

3. Class loader and its delegation mechanism

(1) When the Java virtual machine loads a class, how to load it?
First, the class loader of the current thread loads the first class in the thread (such as class A):

  • If class A references class B, the Java virtual machine will load class A's loader to load class B
  • You can also directly call the ClassLoader.loadClass() method to specify a class loader to load a class

(2) The role of the delegation mechanism - preventing multiple identical bytecodes from appearing in memory.
For example , both class A and class B must load the System class:

  • If the delegation mechanism is not used, and all are loaded by themselves, then class A will load a Sysem bytecode, and class B will also load a bytecode, so that there are two System bytecodes in the memory.
  • If the delegation mechanism is used, it will recursively search for the parent class. It is preferred to use BootStrap to try to load it, and if it can't find it, it will go down. Here System can be found and loaded in BootStrap. If B also loads System at this time, and also from BootStrap. At this time, BootStrap finds that the System bytecode has been loaded, and directly returns the System bytecode in the memory instead of reloading, which ensures that there is only one byte in the memory. code.

For example: the user uses a custom class (without using a custom class loader), then the system starts to send requests from AppClassLoader to the parent class loader, all the way to BootStrap, and then the BootStrap class loader has no parent class, so it starts to search Whether there is a class that meets the requirements in the corresponding path. If not, it will query down again, and finally return to AppClassLoader (the initiator of the request), if there is, it will return, if not, it will throw a ClassNotFoundException exception. If other class loaders have been found before AppClassLoader, it will be returned by the corresponding class load.

Code example 1:

public class ClassLoaderTest {

public static void main(String[] args)   {
    System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
System.out.println("=========================================");
    ClassLoader loader = ClassLoaderTest.class.getClassLoader();
    while(loader != null) {
        System.out.println(loader.getClass().getName());
        loader = loader.getParent();
    }
System.out.println(loader);
    }
}

result:

Code example 2:
First we customize a class:

public class Secret{

    public String key(){
        return "The key is 5132561";
    }
}

Then print the class loader that loads this class:
System.out.println(new Secret().getClass().getClassLoader().getClass().getName());
The output is as follows:

Because from the process of BootStrapExtClassLoaderAppClassLoader, the corresponding class can only be found in AppClassLoader, so AppClassLoader is printed.

Code example 3:
If we type the bytecode of Secret into a jar package and put it in the directory pointed to by ExtClassLoader – /jre/lib/ext, then BootStrapExtClassLoader will find the corresponding class and return to ExtClassLoader, At this time, ExtClassLoader is printed.
Package it into a jar package and save it to the /jre/lib/ext directory.

output again:

System.out.println(new Secret().getClass().getClassLoader().getClass().getName());

2. How (how to customize the class loader?)

A custom class loader must inherit from ClassLoader and implement the overloaded findClass method.

Code example:

public class DecodeClassLoader extends ClassLoader{
    private String classDir;

    public DecodeClassLoader(){}

    public DecodeClassLoader(String classDir){
        this.classDir = classDir;
    }

    @Override
    protected Class<?> findClass(String name) {
        File f = new File(classDir,name.substring(name.lastIndexOf(".") + 1) + ".class");
        try {
            InputStream in = new FileInputStream(f);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            encode(in, out);
            byte[] bytes = out.toByteArray();
            in.close();
            out.close();
            return defineClass(bytes, 0, bytes.length);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }
        return  null;
    }

    private static void encode(InputStream in, OutputStream out) throws IOException{
        int b = 0;
        while((b=in.read())!= -1){
            out.write(b ^ 0xff);
        }
    }   
}

3. Where (where is it used?)

  • Load or unload classes at runtime. Commonly used for:
     Implementing a scripting language
     For use in bean generators
     To allow user-defined extensibility
     To allow communication between named controls.
  • Change the loading of Java bytecode, such as the encryption of Java class bytecode
  • Bytecode modified to load

Fourth, the comprehensive application example of class loader

An important class bytecode is encrypted by an encryption class so that it can only be successfully loaded and used using the decryption class loader. The schematic diagram is as follows:

Code example:

(1) DecodeClassLoader.java see above.
(2) Encryption class: encrypt the Secret bytecode

public class EncodeUtil {
    private static void encode(InputStream in, OutputStream out) throws IOException{
        int b = 0;
        while((b=in.read())!= -1){
            out.write(b ^ 0xff);
        }
    }

    public static void main(String[] args) throws IOException {
        String srcPath = "E:\\java_workspace\\004ClassLoaderDemo\\bin\\com\\shuwoom\\classloader\\Secret.class";//args[0];
        String destDir = "shuwoomlib";

        FileInputStream in = new FileInputStream(srcPath);
        String destFileName = srcPath.substring(srcPath.lastIndexOf("\\") + 1);
        String destPath = destDir + "\\" + destFileName;
        System.out.println(destPath);
        FileOutputStream out = new FileOutputStream(destPath);
        encode(in,out);
        in.close();
        out.close();
    }
}

(3) Loaded classes:

public class Secret{

    public String key(){
        return "The key is 5132561";
    }
}

First run the EncodeUtil encryption tool class to encrypt and save the Secret.class file to the specified shuwoomlib directory. At this time, the Secret in the bin/bom/shuwoom/classloader directory is the unencrypted bytecode. Now we replace Secret.class in the bin/bom/shuwoom/classloader directory with the encrypted Secret.class in the shuwoomlib directory. Then what AppClassLoader finds is the encrypted bytecode.

//If you use AppClassLoader to load directly, an error will be reported.

System.out.println(new Secret().key());

Compiling and running will report an error:

//通过DecodeClassLoader类加载器获得Secret原字节码
ClassLoader classLoader = new DecodeClassLoader("shuwoomlib");
Class clazz = classLoader.loadClass("Secret");

Method getKeyMethod = clazz.getMethod("key");
System.out.println(getKeyMethod.invoke(clazz.newInstance(), null));

It can be used normally at this time.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325937196&siteId=291194637