015. Class loading mechanism

1. Runtime data area

  • The JVM is used to store loaded class information, constants, static variables, compiled code and other data.
  • This is a logical partition in the virtual machine specification. The specific implementation is based on different virtual machines.
  • For example, oracle's HotSpot is placed in the permanent generation in java7, java8 is placed in the original data space, and this area is managed by GC mechanism.

2. Class life cycle

3. Class loader

  • Class loader is responsible for loading classes, searching for specified class resources such as network, jar, zip, folder, binary content, memory, etc.

  • A java program runs with at least three class loader instances, which are responsible for loading different classes.

4. View the loader corresponding to the class

  • View through JDK API: java.lang.Class.getClassLoader()
  • Returns the classloader that loads the class.
  • If the class is loaded by bootstrappclassloader, this method will return null in this implementation.
/**
 * View the loader instance of the class
 */
public class ClassLoaderView {
    public static void main(String[] args) throws ClassNotFoundException {
        // Loading Bootstrap ClassLoader of core class library
        System.out.println("Core class loader:" + ClassLoaderView.class.getClassLoader().loadClass("java.lang.String").getClassLoader());
        // Load Extension ClassLoader of extension library
        System.out.println("Extension class library loader:" + ClassLoaderView.class.getClassLoader().loadClass("com.sun.nio.zipfs.ZipCoder").getClassLoader());
        // Load application's
        System.out.println("Application Loader:" + ClassLoaderView.class.getClassLoader());
        // Parents Delegation Model
        System.out.println("Father of application library loader:" + ClassLoaderView.class.getClassLoader().getParent());
        System.out.println("Parent class of the parent of the application library loader:" + ClassLoaderView.class.getClassLoader().getParent().getParent());
    }
}

5. How does the JVM know where our classes are

  • View the openjdk source code: sun.misc.Launcher.AppClassLoader

  • Conclusion: read the java.class.path configuration and specify which address to load the class resources.

  • Verification process: using jps and jcmd commands

    • jps view native JAVA process.
    • View runtime configuration: jcmd process No. vm.system ﹣ properties
public class Main {
    public static void main(String[] args) throws IOException {
        System.out.println("Hello World!");
        System.in.read(); // block
    }
}

6. Class will not be loaded repeatedly

  • Class uniqueness: the same class loader and class name represent the same class.
  • Identification method: ClassLoader Instance id + PackageName + ClassName.
  • Verification method: use class loader to load different versions of the same class multiple times to check whether the latest code will be loaded.

Class load test class

/* Class load test class */
public class HelloService {

	public static String value = getValue();

	static {
		System.out.println("#### static code ####");
	}

	private static String getValue() {
		System.out.println("#### static method ####");
		return "purplesky...";
	}

	public void test() {
		System.out.println("hello 1111.." + value);
	}
}

Load class code

/**
 * Specify class to load
 */
public class LoaderTest {
    public static void main(String[] args) throws Exception {
        URL classUrl = new URL("file:/Users/shadowolf/Desktop/");

        URLClassLoader loader = new URLClassLoader(new URL[] {classUrl});

        while (true) {
            // Problem: static block trigger
            Class clazz = loader.loadClass("HelloService");
            System.out.println("HelloService Class loader used:" + clazz.getClassLoader());

            Object newInstance = clazz.newInstance();
            Object value = clazz.getMethod("test").invoke(newInstance);
            System.out.println("call getValue The return value obtained is:" + value);

            // Once in 3 seconds
            Thread.sleep(3000);
            System.out.println();
        }
    }
}

Operation steps

  • Compile HelloService.java and run the code to load the class.
  • Modify "hello 1111" in HelloService.java to "hello 2222", and recompile HelloService.java.
  • Then check the running status of the loaded class code and find that there is no update.

7. Class unloading

  • Conditions for class unloading:

    • All instances of this Class have been GC;
    • The ClassLoader instance that loaded the class has been GC.
  • Validation method: add the - verbose:class parameter in the jvm startup to output the log information of class loading and unloading.

Test code

/**
 * Specify class to load
 */
public class LoaderTest {
    public static void main(String[] args) throws Exception {
        URL classUrl = new URL("file:/Users/shadowolf/Desktop/");

        // Create a classloader
        URLClassLoader loader = new URLClassLoader(new URL[]{classUrl});

        while (true) {
            if (loader == null) break;

            // Problem: static block trigger
            Class clazz = loader.loadClass("HelloService");
            System.out.println("HelloService Class loader used:" + clazz.getClassLoader());

            Object newInstance = clazz.newInstance();
            Object value = clazz.getMethod("test").invoke(newInstance);
            System.out.println("call getValue The return value obtained is:" + value);

            // Once in 3 seconds
            Thread.sleep(3000);
            System.out.println();

            //  help gc  -verbose:class
            newInstance = null;
            loader = null;
        }

        System.gc();
        Thread.sleep(10000);
    }
}

Result

8. Parent delegation model

  • In order to avoid repeated loading, level by level delegation from bottom to top and level by level search from top to bottom.
  • First of all, I will not try to load the class myself, but delegate the request to the parent loader to complete it; this is true for every level of loader, so all class loading requests will be passed to the upper startup class loader.
  • Only when the parent loader feedback that it cannot complete the load request (no corresponding class is found in the loader's search scope), the child loader will try to load by itself.
  • Note: there is no parent-child relationship between class loaders. "Parents" is a translation, which can be understood as a logically defined parent-child relationship.

Test code

/**
 * Specify class to load
 */
public class LoaderTest {
    public static void main(String[] args) throws Exception {
        URL classUrl = new URL("file:/Users/shadowolf/Desktop/");

        // Create a classloader
        URLClassLoader parentLoader = new URLClassLoader(new URL[] {classUrl});

        while (true) {
            // The parent loader is specified here as parentLoader, so HelloService will be loaded by the parent loader
            // Even if the loader object is recreated every time here, HelloService cannot do the hot load
            URLClassLoader loader = new URLClassLoader(new URL[] {classUrl}, parentLoader);

            // Problem: static block trigger
            Class clazz = loader.loadClass("HelloService");
            System.out.println("HelloService Class loader used:" + clazz.getClassLoader());

            Object newInstance = clazz.newInstance();
            Object value = clazz.getMethod("test").invoke(newInstance);
            System.out.println("call getValue The return value obtained is:" + value);

            // Once in 3 seconds
            Thread.sleep(3000);
            System.out.println();

            //  help gc  -verbose:class
            newInstance = null;
            loader = null;
        }
    }
}
  • In the above code, because the HelloService will be loaded by the parent loader, even if a loader object is recreated every time in the while loop, the HelloService file cannot be hot loaded.

Tags: Programming Java jvm Oracle network

Posted on Wed, 06 May 2020 22:02:52 -0700 by reinaldo