JVM 类加载器_3

启动类加载器深入

     内建于JVM中的启动类加载器会加载java.lang.ClassLoader以及其他的Java平台类。 当JVM启动时,一块特殊的机器码会运行,它会加载扩展类加载器与系统类加载器,这块特殊的机器码叫做启动类加载器(Bootstrap)
     启动类加载器并不是java类,而其他的加载器则都是java类
     启动类加载器是特定于平台的机器指令,它负责开启整个加载过程。
     所有类加载器(除了启动类加载器)都被实现为Java类,不过,总归要有一个组件来加载第一个Java类加载器。从而让整个加载过程能够顺利进行下去,加载第一个纯Java类加载器就是启动类加载器的职责。
     启动类加载器还会负责加载供JRE正常运行所需要的基本组件,这包括java.util与java.lang包中的类等等。

     System.out.println(ClassLoader.class.getClassLoader());
     结果为Null。说明ClassLoader类也是启动类加载器加载的。

     System.out.println(Launcher.class.getClassLoader());
     由于扩展类加载器、系统类加载器都是Launcher的静态内部类,不是public的,并不能访问到。而我们之前根据例子MySample了解到,MyCat是由MySample加载的类加载器进行加载的,同理,系统类加载器、扩展类加载器也是由Launcher的类加载器进行加载的,所以用以上语句能够证实:扩展类加载器、系统类加载器都是由启动类加载器进行加载的

自定义系统类加载器

先自己读一下Doc文档

JVM 类加载器_3_第1张图片

译文

Ps: 自己翻译的,如果有错麻烦指出啦~谢谢~

     返回用于委托的系统类加载器。它是新的ClassLoader实例的默认的委托双亲,它是用来启动应用的类加载器的典范。
     这个方法最早在运行时的启动序列(阶段?)中被第一次调用,在这个时间点它会创建系统类加载器并且它会将它设置为调用这个方法的线程的上下文类加载器。(这边总感觉怪怪的)
     默认的系统类加载器是一个与这个类实现相关的的实例。
     如果系统属性java.system.class.loader在getSystemClassLoader方法第一次调用的时候已经被定义了(也就是给了初值),那么这个属性定义的类将会作为这个方法返还的系统类加载器的类的名字。被定义了这个属性的类是被默认的系统类加载器加载的,并且这个类必须定义一个public的带一个ClassLoader参数的构造方法,用来作为委托双亲。然后用默认的系统类加载器作为参数的这个构造方法将会创建一个实例,这个作为结果的类加载器将会被定义为系统类加载器。

那么我们来试验一下

     首先我们自定义一个类加载器,用我们之前的例子:

package com.ssy.jvm.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 创建了一个自己定义的类加载器,该例子用的是系统默认的AppClassLoader做为其双亲
 * 用自己定义的方法来加载类,生成类的实例
 */
public class MyFirstClassLoader extends ClassLoader {
    private final String extSuffix = ".class";
    private String classloaderName;
    private String path;

    /**
     * ClassLoader的doc文档中说明 Each instance of ClassLoader has an associated parent class loader.
     * 翻译:每个ClassLoader的实例都会有一个与之关联的父类加载器。
     */
    public MyFirstClassLoader(String classloaderName) {
        // super();可加可不加,会自动调用父类不带参数的构造方法
        // 加上是为了提醒自己这边用的是系统类加载器(引用类加载器)作为MyFirstClassLoader的双亲;
        super();
        this.classloaderName = classloaderName;
    }

    public MyFirstClassLoader(ClassLoader classLoader) {
        super(classLoader);
    }
    
    public MyFirstClassLoader(String classloaderName, ClassLoader classLoader) {
        // 这边是用自己定义的类加载器作为MyFirstClassLoader的双亲
        super(classLoader);
        this.classloaderName = classloaderName;
    }

    public void setPath(String path) {
        this.path = path;
    }

    /**
     * ClassLoader类的loadClass方法里会调用findClass方法来加载这个类。
     *
     * @param className 类的二进制名字(ClassLoader类的doc中有解释binary name。类似于java.lang.String)
     * @return 该name所对应的类的Class对象
     */
    @Override
    protected Class<?> findClass(String className) {
        System.out.println("findClass invoked: " + className);
        System.out.println("class loader name: " + this.classloaderName);
        byte[] data = loadClassData(className);
        // 调用父类的defineClass方法来返回该类对应的Class对象
        return this.defineClass(className, data, 0, data.length);
    }

    /**
     * 用自己定义的方法来加载类的二进制文件
     *
     * @param className 类的二进制名字(ClassLoader类的doc中有解释binary className。类似于java.lang.String)
     * @return 加载后的二进制数组
     */
    public byte[] loadClassData(String className) {
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        byte[] data = null;
        // mac系统换成 /
        className = className.replace(".", "/");

        try {
            is = new FileInputStream(new File(this.path + className + this.extSuffix));
            baos = new ByteArrayOutputStream();

            int ch;

            while ((ch = is.read()) != -1) {
                baos.write(ch);
            }

            data = baos.toByteArray();

        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                is.close();
                baos.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return data;
    }

    public static void main(String[] args) throws Exception {
        MyFirstClassLoader loader1 = new MyFirstClassLoader("loader1");
//        loader1.setPath("/Users/ddcc/IdeaProjects/jvm_lecture/out/production/classes/");
        loader1.setPath("/Users/ddcc/Desktop/");
        Class<?> clazz = loader1.loadClass("com.ssy.jvm.classloader.MyTest1");
        System.out.println("class: " + clazz.hashCode());
        Object object = clazz.newInstance();
        System.out.println(object);
        System.out.println();

        loader1 = null;
        clazz = null;
        object = null;

        System.gc();
        Thread.sleep(200000);

        loader1 = new MyFirstClassLoader("loader1");
        loader1.setPath("/Users/ddcc/Desktop/");
        clazz = loader1.loadClass("com.ssy.jvm.classloader.MyTest1");
        System.out.println("class: " + clazz.hashCode());
        System.out.println(clazz.newInstance());

        System.out.println();
    }

    /**
     * 测试
     *
     * @param classLoader 类加载器
     */
    public void test(ClassLoader classLoader) throws Exception {
        Class<?> clazz = classLoader.loadClass("com.ssy.jvm.classloader.MyTest1");
        System.out.println(clazz.newInstance());
        System.out.println(clazz.getClassLoader());
    }
}

注意:这个自定义的类加载器需要有一个public的带ClassLoader参数的构造方法(文档中讲的)。

自定义一个测试类

package com.ssy.jvm.classloader;

/**
 * 在运行期,一个Java类是由该类的完全限定名(binary name,二进制名)
 * 和用于加载该类的定义类加载器(defining loader)所共同决定的。
 * 如果同样名字(即相同的完全限定名)的类是由两个不同的加载器所加载,那么这些类就是不同的。
 * 即便.class文件的字节码完全一样,并且从相同的位置加载亦是如此。
 */

import sun.misc.Launcher;

public class MyTest23 {
    public static void main(String[] args) {
        // 启动类加载器加载路径
        System.out.println(System.getProperty("sun.boot.class.path"));
        // 扩展类加载器加载路径。
        System.out.println(System.getProperty("java.ext.dirs"));
        // 应用类加载器加载路径。
        System.out.println(System.getProperty("java.class.path"));

        System.out.println(ClassLoader.class.getClassLoader());
        // 扩展类加载器与系统类加载器也是由启动类加载器所加载的。
        // 这边打出的Launcher类的类加载器可以说明系统类加载器和启动类加载器的类加载器,
        // 因为扩展类加载器和应用类加载器都是Launcher类内部的静态类,MySample例子可以知道,其内部类也是由加载Launcher的加载器进行加载的
        System.out.println(Launcher.class.getClassLoader());

        System.out.println("-------------------------------");
        // 看一下getSystemClassLoader方法的DOC,
        // 其中说明如果指定java.system.class.loader,那么会让系统类加载器指向我们自己定义的加载器
        ClassLoader.getSystemClassLoader();
        // 在默认情况下,这个是没有被定义的。该属性是没有被定义的,系统类加载器默认会指向AppClassLoader
        System.out.println(System.getProperty("java.system.class.loader"));
        System.out.println(MyTest23.class.getClassLoader());
        // 成为了系统类加载器的自定义加载器内部其实还是系统类加载器进行加载的。文档里也有说。
        System.out.println(MyFirstClassLoader.class.getClassLoader());
        // 现在让我们自己定义的加载器成为系统类加载器,
        // 我们自己定义的类加载器必须定义一个public的一个ClassLoader参数的构造方法(Doc中说的)给系统调用的
        // MyFirstClassLoader中加一个构造方法 super(classLoader)
        // java -Djava.system.class.loader=com.ssy.jvm.classloader.MyFirstClassLoader com.ssy.jvm.classloader.MyTest23
        System.out.println(ClassLoader.getSystemClassLoader());
        // 上面那一行,在控制台上执行就会变成我们自己定义的类加载器了
    }
}

现在我们在Terminal终端中进行运行:
切入到classes目录下:

DDCCdeMacBook-Pro:classes ddcc$ pwd
/Users/ddcc/IdeaProjects/jvm_lecture/out/production/classes

运行:

java -Djava.system.class.loader=com.ssy.jvm.classloader.MyFirstClassLoader com.ssy.jvm.classloader.MyTest23

结果:

/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/classes
/Users/ddcc/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
.
null
null
-------------------------------
com.ssy.jvm.classloader.MyFirstClassLoader
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
com.ssy.jvm.classloader.MyFirstClassLoader@4e25154f

可以发现,最后一行ClassLoader获得的系统类加载器变成了我们自定义的类加载器了。

你可能感兴趣的:(JVM)