JVM类加载器分类和解析

类加载器

类加载器的分类
  • JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Definded ClassLoader)
  • 从概念上来讲,自定义类加载器是程序中由开发人员定义的一类类加载器,但是Java虚拟机规范当中却没这么定义,而是把所有的派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。
  • 无论类加载器的类型如何划分,在程序当中最常见的类加载器始终只有3个,**引导类加载器、扩展类加载器、系统类加载器。**其中除了引导类加载器,剩下的都是派生于ClassLoader的类,也就是自定义类加载器。

类加载器的获取代码演示

public class ThreadTest {
    public static void main(String[] args) {
        //获取系统类加载器  sun.misc.Launcher$AppClassLoader@18b4aac2
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //获取上层的扩展类加载器  sun.misc.Launcher$ExtClassLoader@1540e19d
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);

        //获取上层的引导类加载器  null
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(bootstrapClassLoader);

        //对于用户自定义类来说  sun.misc.Launcher$AppClassLoader@18b4aac2
        ClassLoader classLoader = ThreadTest.class.getClassLoader();
        System.out.println(classLoader);
    }
}
启动类加载器(引导类加载器,Bootstra ClassLoader)
  • 这个类加载使用C/C++语言实现的,嵌套在JVM内部
  • 这个加载器用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar、resources.jar 或者是 sun.boot.class.path路径下的内容),用于提供JVM的自身需要的类
  • 并不继承自java.lang.ClassLoader,没有父类加载器。
  • 加载扩展类和应用程序类加载器,并指定为他们的父类加载器
  • 出于安全考虑,BootStrap启动类加载器自家在包名为java、javax、sun等开头的类
扩展类加载器(Extension ClassLoader)
  • Java语言编写,由sun.misc.Launcher$ExtClassLoader实现
  • 派生于ClassLoader类
  • 父类加载器为启动类加载器
  • 从java.ext.dirs系统属性所指定的目录中加载类库,或者从JDK的按抓个目录的jre/lib/ext 子目录(扩展目录)下加载类库。如果用户创建的JAR放在次目录下,也会自动由扩展类加载器加载。

下面对类的加载器进行测试:

package com.classload.chapter01;

import sun.misc.Launcher;
import sun.security.ec.CurveDB;

import java.net.URL;

public class ThreadTest {
    public static void main(String[] args) {
        System.out.println("=====启动类加载器=====");
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (URL url : urLs) {
            System.out.println(url);
        }

        System.out.println("=====扩展类加载器=====");
        String property = System.getProperty("java.ext.dirs");
        for (String path : property.split(";")) {
            System.out.println(path);
        }

        System.out.println("=====类加载器测试=====");
        System.out.println("测试的类: CurvDB");
        ClassLoader classLoader = CurveDB.class.getClassLoader();
        System.out.println(classLoader);
    }
}
# 测试结果
=====启动类加载器=====
file:/D:/JDK/jdk1.8.0_131/jre/lib/resources.jar
file:/D:/JDK/jdk1.8.0_131/jre/lib/rt.jar
file:/D:/JDK/jdk1.8.0_131/jre/lib/sunrsasign.jar
file:/D:/JDK/jdk1.8.0_131/jre/lib/jsse.jar
file:/D:/JDK/jdk1.8.0_131/jre/lib/jce.jar
file:/D:/JDK/jdk1.8.0_131/jre/lib/charsets.jar
file:/D:/JDK/jdk1.8.0_131/jre/lib/jfr.jar
file:/D:/JDK/jdk1.8.0_131/jre/classes
=====扩展类加载器=====
D:\JDK\jdk1.8.0_131\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
=====类加载器测试=====
测试的类: CurvDB
sun.misc.Launcher$ExtClassLoader@29453f44

上述代码的解释:
1、获取引导类加载器的扫描目录的时候,使用sun.misc.Launcher.getBootstrapClassPath().getURLs();方法获取扫描的所有jar包的目录
2、获取扩展类加载器的扫描路径的时候使用的是System.getProperty("java.ext.dirs");方法因为系统属性的java.ext.dirs当中加载类库
3、使用一个类去测试他的类加载器使用class的getClassLoader方法,因为CurvDB是D:\JDK\jdk1.8.0_131\jre\lib\ext目录下的sunec包下的一个类,所以使用的类加载器是扩展类加载器。

但是假如说找String这个核心包下的类呢?来试一试

public static void main(String[] args) {
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1);
}

测试结果:null

因为核心包走的都是引导类加载器,而且引导类加载器都是C和C++写的,所以说获取不到。

用户自定义类加载器

用户自定义类加载器的实现步骤:
1、开发者会根据抽象类java.lang.ClassLoader类的方式,实现自己的类加载器,来满足一些需求。
2、在JDK1.2之前,在自定义类加载器时,总会去继承ClassLoader类并重写loadClass() 方法,从而实现自定义的类加载器,但是在JDK1.2之后已经不再建议用户去覆盖loadCass() 方法,而是建议把自定义的类加载逻辑写到findClass() 方法当中。
3、在编写自定义类加载器的时候,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样就可以避免自己去编写findClass() 方法机器获取字节码流的方式,是自定义类加载器编写更加的简洁。

下面写一段代码来理解一下:

//CustomClassLoader.java类
public class CustomClassLoader extends ClassLoader {
    private String path;

    /**
     * @param path 需要加载的class文件的路径
     */
    public CustomClassLoader(String path) {
        this.path = path;
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] result = getClassData();
            if(result == null) {
                throw new FileNotFoundException();
            } else {
                return defineClass(name, result, 0, result.length);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }

    /**
     * @return  返回class文件的二进制流
     *
     * 把文件读出的数据放到内存的二进制输出流
     * 把二进制输出流返回成byte数组
     *
     * 这里使用构造函数的path来加载.class文件
     */
    private byte [] getClassData() {
        File file = new File(path);
        if(file.exists()) {
            InputStream in = null;
            ByteArrayOutputStream out = null;
            try {
                in = new FileInputStream(file);
                out = new ByteArrayOutputStream();

                int length = 0;
                byte [] bytes = new byte[1024];
                while ((length = in.read(bytes)) != -1) {
                    out.write(bytes, 0, length);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return out.toByteArray();
        } else {
            return null;
        }
    }
}

测试代码:

public class ThreadTest {
    public static void main(String[] args) throws ClassNotFoundException {
        CustomClassLoader loader = new CustomClassLoader("E:\\IDEA\\JavaBase\\ClassLoad\\target\\classes\\com\\classload\\chapter01\\HelloLoader.class");
        Class<?> aClass = loader.loadClass("com.classload.chapter01.HelloLoader");
        System.out.println("类加载器是:" + aClass.getClassLoader());
        Object object = aClass.newInstance();
        System.out.println(object);
    }
}

# 输出样例:
类加载器是:sun.misc.Launcher$AppClassLoader@18b4aac2
execute
com.classload.chapter01.HelloLoader@677327b6

1、首先需要使用类的路径加载.class 文件,把class文件转换为二进制数组
2、根据二进制数组和文件的包名+文件名,调用ClassLoader类的defineClass方法,完成类的加载过程

在类加载完成之后,可以调用newInstance() 方法来获取类的实例,使用getClassLoader()方法可以获取类的加载器

好了,类的三种加载器就写到这里,感谢你能看到最后,谢谢支持。
对了,如果哪里写的不对,在下面的评论区跟我说一下哈。886

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读