The reason for writing this article is just a bit of weird looking code that makes me feel the need to get acquainted with ClassLoader

----[Counter.java]------------------------- public class Counter { private static Counter sCounter = new Counter(); //<---- tag1 public static int count = 10; //<---- tag2 privateCounter() {
        count++;
    }
    public static Counter getInstance() {
        returnsCounter; } } ----[Client.java]------------------------- public class Client { public static void main(String[] args) { Counter counter = Counter.getInstance(); System.out.println(counter.count); / / 10}} | - when tag1 and tag2 swap places, get is 11Copy the code

1. Java class loading process

1.Java VM structure

In the last article, I talked about the Java Virtual Machine, so I didn’t talk about class loaders, but here I’ll talk about how Java files can be compiled into.class files using Javac. Class loaders are the ones that load.calss into memory


2. Class loading process

Is the Class instance in the heap or the method area? I found an article here, which goes quite deep


2.1: load
Generate a Java.lang.class object by loading the bytecode (binary stream) into the method area heap memory, Area as a method of operation of the various data entry. | - class main source file -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - disk loaded directly - - network load. The class file - - from the zip, - Extract. Class files from a proprietary database - dynamically compile Java source files into. Class filesCopy the code

2.2: connection-authentication

Verify that the loaded byte stream information complies with virtual machine specifications

[1]. File format verification: Whether the byte stream complies with the class file format specification [2]. Metadata validation: whether it conforms to the specification of Java language syntax [3]. Bytecode validation: the method body is verified and analyzed to ensure that no hazards occur at runtime [4]. Symbol reference verification: Matches the symbol reference information in the constant poolCopy the code

2.3: Connection-prepare

Allocate memory for class static variables and set to [initial value of corresponding type]

----[Counter.java]-------------------------
public class Counter {
    private static Counter sCounter = new Counter();
    public static int count = 1;
    private Counter() {
        count++;
    }
    public static Counter getInstance() {
        returnsCounter; }} The default value of int is 0Copy the code

2.4: connection-resolution

The process of replacing a symbolic reference in a constant pool with a direct reference, i.e. converting a literal to a pointer. Main parsing: class, interface, field, class method, interface method, method type, method handle and call point qualifier reference


2.5: initialization

Find static variables and static block assignments to user-defined class variables in order,

Private static Counter sCounter = new Counter(); private static Counter sCounter = new Counter(); public static int count = 10; // In this case, count is set to 10Copy the code

When the class is initialized

1. Code test when the class is initialized
2. Access or assign a value to a static variable. 3. ---->[Shape]------------------ public class Shape {public static String color ="White";
    static {
        System.out.println("----- initialized on Shape-----");
    }
    public static void draw() {
    }
}

---->[Shape子类:Rect]------------------
public class Rect extends Shape {
    public static int radius = 20;
    static {
        System.out.println("----- initialized in Rect-----"); } } new Shape(); //1. Create instance String color = shape.color; //2. Access the static variable shape.color ="Black"; //2. Assign the static variable shape.draw (); //3. Call the static method class.forname ()"classloader.Shape"); //4. Rect.radius = 10; //5. Initialize a subclass of a classCopy the code

2. Impact of final on initialization
| - access compile-time constants [wouldn't] static initialization trigger | - static access runtime constants [will] initialization trigger public class Shape {... public static final int weight = 1; public static final int height = new Random(10).nextInt(); . } int w = Shape.weight; // Static constants at compile time do not trigger initialization int h = shape.height; / / static constants will trigger the run-time initialization | - height at runtime to determine value, access will trigger the initializationCopy the code

3. Other initialization points
| - class initialization time does not initialize the interface | - child interface initialization not initialize the parent interface class variables will not initialize | | - statement - subclass to call a static method or property of the parent, child will not be initialized Shape Shape; String color = rect.color; // Declare a class variable that will not be initialized. // Only initialize Shape rect.draw (); // Initialize Shape onlyCopy the code

About class loaders

1. System class loader (application class loader)

Through this. GetSystemClassLoader () to get class loader debug the system, you can see the system class loader: class called AppClassLoader, so also known as the application class loader

ClassLoader loader = ClassLoader.getSystemClassLoader();
System.out.println(loader);

Shape shape = new Shape();
////sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader loader = shape.getClass().getClassLoader();

String name = "toly"; ClassLoader loaderSting = name.getClass().getClassLoader(); System.out.println(loaderSting); / / null / / visible String class loader is null, first, to null by the Bootstrap class loader loads | - also want to emphasize that class loader loads after class, Will not trigger a class initialization this loader. = this getSystemClassLoader (); Class<? > shapeClazz = loader.loadClass("classloader.Shape"); Shape = (Shape) shapeclazz.newinstance (); // This is initialized when the instance is createdCopy the code

2. Parent delegation mechanism (or parent delegation mechanism)

The parent property in the ClassLoader class is of type ClassLoader, so it is the parent property, not the parent property. Just like the parent-child View relationship between ViewGroup and View in Android, after the recognition of the father, there is a prior to let the father to deal with the problem, the father is not, and then come to, both are not, then broken.

---->[ClassLoader# member variable]----------------
private final ClassLoader parent;

---->[ClassLoader# Constructor a parameter]----------------| - can be found in a constructor to the parent, recognize a andie, glanced at the source code, Protected ClassLoader(ClassLoader parent) {this(checkCreateClassLoader(), parent); } | - about father entrusted mechanism perfect loadClass method: -- -- -- - > [this#loadClass]------------------public Class<? > loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);
}

---->[ClassLoader#loadClass(String,boolean)]------------------------------protected Class<? > loadClass(String name, boolean resolve) throws ClassNotFoundException{ synchronized (getClassLoadingLock(name)) { // First, checkifThe class has already been loaded-- Check whether the class <? > c = findLoadedClass(name);if(c == null) {// not loaded long t0 = system.nanotime (); try {if(parent ! = null) {c = parent.loadclass (name, name);false);
                } elseC = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { }if(c == null) {long t1 = 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(); }}if (resolve) {
            resolveClass(c);
        }
        returnc; }}Copy the code


3. Class loaders in three JVMS

The Bootstrap this: the Bootstrap class loader (start class loader/root class loader) | - c + + language implementation, responsible for loading the jre/lib directory at the core of the library System. Out.println (System. GetProperty ("sun.boot.class.path")); M / / D: \ \ JDK1.8 \ jre \ lib \ resources jar; M / / D: \ \ JDK1.8 \ jre \ lib \ rt jar; M / / D: \ \ JDK1.8 \ jre \ lib \ sunrsasign jar; M / / D: \ \ JDK1.8 \ jre \ lib \ jsse jar; M / / D: \ \ JDK1.8 \ jre \ lib \ jce jar; M / / D: \ \ JDK1.8 \ jre \ lib \ charsets jar; M / / D: \ \ JDK1.8 \ jre \ lib \ JFR jar; M / / D: \ \ JDK1.8 \ jre \ classes the Launcher$ExtClassLoader: expand the class loader | - Java language implementation, responsible for loading the jre/lib/ext System. Out.println (System. GetProperty ("java.ext.dirs")); M / / D: \ \ JDK1.8 \ jre \ lib \ ext. C:\Windows\Sun\Java\lib\ext Launcher$AppClassLoader: the System class loader | - Java language implementation, loading the classpath environment variable path or Java. The class. The path class libraries under the specified path String property = System. GetProperty ("java.class.path"); M / / D: \ \ JDK1.8 \ jre \ lib \ charsets jar; M / / D: \ \ JDK1.8 \ jre \ lib \ deploy the jar. . Some JRE JAR paths are omitted... // J:\FileUnit\file_java\base\out\production\classes; // C:\Program Files\JetBrains\IntelliJ IDEA 2018.1.3\lib\idea_rt.jarCopy the code

Custom class local disk class loader

1. The parent of custom class loaders
---->[ClassLoader# constructor]------------------------------------------
protected ClassLoader(ClassLoader parent) {
    this(checkCreateClassLoader(), parent);
}

protected ClassLoader() { this(checkCreateClassLoader(), getSystemClassLoader()); } here you can see no arguments structure is the default andie: getSystemClassLoader, also is the system class loader loader , of course, also can use a reference structure to recognize andie | - the above analysis: in this#loadClass method when none of the three JVMS can be found| - will call findClass method to initialize c, then we look at the findClass: -- -- -- - > [in this#findClass]------------------------protected Class<? > findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); } just ask you: someone else directly throw abnormal, you dare not write?Copy the code

2. Customize LocalClassLoader
/** * Author: Zhang Feng Jiete Lie * Time: 2019/3/7/007:14:05 * Email: [email protected] * Description: Public class LocalClassLoader extends ClassLoader {private String path; public LocalClassLoader(String path) { this.path = path; } @Override protected Class<? > findClass(String name) { byte[] data = getBinaryData(name);if (data == null) {
            return null;
        }
        returndefineClass(name, data, 0, data.length); } /** * Read byte stream ** @param name Full class name * @return*/ private byte[] getBinaryData(String name) {InputStream is = null; byte[] result = null; ByteArrayOutputStream baos = null; try {if (name.contains(".")) {
                String[] split = name.split("\ \.");
                name = split[split.length - 1];
            }
            String path = this.path + "\ \" + name + ".class";
            File file = new File(path);
            if(! file.exists()) {return null;
            }
            is = new FileInputStream(file);
            baos = new ByteArrayOutputStream();
            byte[] buff = new byte[1024];
            int len = 0;
            while((len = is.read(buff)) ! = -1) { baos.write(buff, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { try {if(is ! = null) { is.close(); }if (baos != null) {
                    result = baos.toByteArray();
                    baos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        returnresult; }}Copy the code

3. Test the class’s bytecode file

Create a new class HelloWorld with a public method say, notice the package name and folder name

package com.toly1994.classloader;
public class HelloWorld {
    public void say() {
        System.out.println("HelloWorld"); }}Copy the code

4. Use LocalClassLoader

Use the LocalClassLoader to load the bytecode file. Use the reflection to call the say method, and it will work correctly

LocalClassLoader loader = new LocalClassLoader("G:\\Out\\java\\com\\toly1994\\classloader"); try { Class<? > clazz = loader.loadClass("com.toly1994.classloader.HelloWorld");; Constructor<? > constructor = clazz.getConstructor(); Object obj = constructor.newInstance(); Method say = clazz.getMethod("say"); say.invoke(obj); //HelloWorld } catch (NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); } | - here you can test the obj class loader System. Out. The println (obj) getClass () getClassLoader ()); //classloader.LocalClassLoader@6b71769eCopy the code

This way, no matter where the. Java file is moved to the disk, it can be loaded through the specified path


Custom class network class loaders

Will just the class file on the server: www.toly1994.com:8089/imgs/HelloW… Then access the path to read the byte stream and load the class

1. Customize the NetClassLoader

The core idea is to take a stream and generate a Class object from defineClass in findClass

/** * Author: Zhang Feng Jiete Lie * Time: 2019/3/7/007:14:05 * Email: [email protected] * Description: */ public class NetClassLoader extends ClassLoader {private String urlPath; public NetClassLoader(String urlPath) { this.urlPath = urlPath; } @Override protected Class<? > findClass(String name) { byte[] data = getDataFromNet(urlPath);if (data == null) {
            return null;
        }
        return defineClass(name, data, 0, data.length);
    }
    private byte[] getDataFromNet(String urlPath) {
        byte[] result = null;
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        try {
            URL url = new URL(urlPath);
            is = url.openStream();
            baos = new ByteArrayOutputStream();
            byte[] buff = new byte[1024];
            int len = 0;
            while((len = is.read(buff)) ! = -1) { baos.write(buff, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { try {if(is ! = null) { is.close(); }if (baos != null) {
                    result = baos.toByteArray();
                    baos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        returnresult; }}Copy the code

2. Use

Basically consistent in use

NetClassLoader loader = new NetClassLoader("http://www.toly1994.com:8089/imgs/HelloWorld.class"); try { Class<? > clazz = loader.loadClass("com.toly1994.classloader.HelloWorld"); Constructor<? > constructor = clazz.getConstructor(); Object obj = constructor.newInstance(); Method say = clazz.getMethod("say"); say.invoke(obj); //HelloWorld } catch (NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); } | - here you can test the obj class loader System. Out. The println (obj) getClass () getClassLoader ()); //classloader.NetClassLoader@66d2e7d9Copy the code

3. Parent delegate mechanism testing

Now that both network and local are available, we let the local loader act as the father of the network load

---->[NetClassLoader# Add construct]------------------------public NetClassLoader(ClassLoader parent, String urlPath) { super(parent); this.urlPath = urlPath; } -- -- -- - > [test class] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- LocalClassLoaderlocalLoader = new LocalClassLoader("G:\\Out\\java\\com\\toly1994\\classloader"); // The parent of NetClassLoader is set tolocalLoader
NetClassLoader netLoader = new NetClassLoader(localLoader, "http://www.toly1994.com:8089/imgs/HelloWorld.class"); try { Class<? > clazz = netLoader.loadClass("com.toly1994.classloader.HelloWorld"); Constructor<? > constructor = clazz.getConstructor(); Object obj = constructor.newInstance(); System.out.println(obj.getClass().getClassLoader()); / / here to print this. LocalClassLoader @ 591 f989e Method say = clazz. GetMethod ("say"); say.invoke(obj); //HelloWorld } catch (NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); } | - you can see, torre LocalClassLoader can load, as the child's NetClassLoader didn't load | -- - will now be local [deleted], torre LocalClassLoader won't load, System.out.println(object.getClass ().getClassLoader()); classloader.NetClassLoader@4de8b406Copy the code

Now you see how the parent delegate mechanism works. If NetClassLoader can’t load it, it crashes


Uninstalling class objects

1. The condition that a class can be collected (i.e., unloaded) by GC
[1]. All instances of this class have been GC. [2]. The ClassLoader instance that loaded this class has been GC. [3]. Java.lang.Class objects of this Class are not referenced anywhere.Copy the code

2. References in the JVM when using a custom loader

LocalClassLoader localLoader = new LocalClassLoader("G:\\Out\\java\\com\\toly1994\\classloader"); Class<? > clazz =localLoader.loadClass("com.toly1994.classloader.HelloWorld"); Constructor<? > constructor = clazz.getConstructor(); Object obj = constructor.newInstance(); System.out.println(obj.getClass().getClassLoader()); Method say = clazz.getMethod("say"); say.invoke(obj); / / HelloWorld | - using the above class loader loads again. Com toly1994. This. The two visible HelloWorld class object consistent System. Out. The println (clazz. HashCode ()); //1265210847 Class<? > clazz2 =localLoader.loadClass("com.toly1994.classloader.HelloWorld"); System.out.println(clazz2.hashCode()); / / 1265210847Copy the code

2. The unloading
LocalClassLoader localLoader = new LocalClassLoader("G:\\Out\\java\\com\\toly1994\\classloader"); Class<? > clazz =localLoader.loadClass("com.toly1994.classloader.HelloWorld"); Constructor<? > constructor = clazz.getConstructor(); Object obj = constructor.newInstance(); Method say = clazz.getMethod("say"); say.invoke(obj); Obj = null; // Clear instances of this classlocalLoader = null; // Clear the ClassLoader reference for the class clazz = null; // Clear the reference to the class objectCopy the code

Postscript: Jie wen standard

Reference article:

Classloaders create, load, and unload classes. Do class instances exist in the heap or in the method area?


1. Growth record and Errata of this paper
Program source code The date of The appendix
V0.1, The 2018-3-7 There is no

Release name: ClassLoader such as JVM

2. More about me
Pen name QQ WeChat
Zhang Feng Jie te Li 1981462002 zdl1994328

My github:github.com/toly1994328 Jane books: www.jianshu.com/u/e4e52c116… I’m Jane books: www.jianshu.com/u/e4e52c116… Personal website: www.toly1994.com

3. The statement

1—- this article is originally written by Zhang Feng Jetelie, please note 2—- all programming enthusiasts are welcome to communicate with each other 3—- personal ability is limited, if there is any error, you are welcome to criticize and point out, will be modest to correct 4—- See here, I thank you here for your love and support