JVM-类加载器

4 篇文章 0 订阅

JVM支持两种类型的类加载器,分别是启动类加载器引导类加载器,Bootstrap ClassLoader)和自定义加载器User-Defined ClassLoader)。如下图
在这里插入图片描述

从概念上来讲,自定义类加载器一般是指程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载都划分为自定义类加载器。(Extension ClassLoader系统类加载器 、系统类加载器System ClassLoader间接继承了Class Loader,也属于自定义加载器)

sun.misc.Launcher是一个Java虚拟机的入口应用,打开Launcher.class文件可以看到如下关系:
在这里插入图片描述
代码验证示例,如下:

class SonOfClassLoader extends ClassLoaderDemo{ }

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

        //获取其上层得到扩展类加载器
        ClassLoader extClassLoader=systemClassLoader.getParent();
        System.out.println("获取系统类加载器的上层:"+extClassLoader);
        //sun.misc.Launcher$ExtClassLoader@677327b6

        //再获取其上层:获取不到引导类加载器
        ClassLoader bootstrapClassLoader=extClassLoader.getParent();
        System.out.println("获取系统类加载器的上上层:"+bootstrapClassLoader);
        //null

        //对于用户自定义类来说:默认使用系统类加载器进行加载,所以可以看到地址都为@18b4aac2
        System.out.println("用户自定义类的类加载器:"+ClassLoaderDemo.class.getClassLoader());
        //sun.misc.Launcher$AppClassLoader@18b4aac2

        ClassLoader sonClassLoader= SonOfClassLoader.class.getClassLoader();
        System.out.println("用户自定类子类的类加载器:"+sonClassLoader);
        //sun.misc.Launcher$AppClassLoader@18b4aac2,验证了第一张图
        System.out.println("用户自定类子类的父类类加载器:"+sonClassLoader.getParent());
        //sun.misc.Launcher$ExtClassLoader@677327b6,验证了第一张图

        //可见String类使用引导类加载器进行加载。--》Java的核心类库都是使用引导类加载器进行加载
        System.out.println("java.lang.String类的类加载器:"+String.class.getClassLoader());
        //null
        System.out.println("java.lang.ClassLoader类的类加载器:"+ClassLoader.class.getClassLoader());
        //null
    }
}

运行结果:

获取系统类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
获取系统类加载器的上层:sun.misc.Launcher$ExtClassLoader@677327b6
获取系统类加载器的上上层:null
用户自定义类的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
用户自定类子类的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
用户自定类子类的父类类加载器:sun.misc.Launcher$ExtClassLoader@677327b6
java.lang.String类的类加载器:null
java.lang.ClassLoader类的类加载器:null

JVM自带的加载器

  • 启动类加载器(引导类加载器,Bootstrap ClassLoader)

    这个类加载器使用C/C++语言实现的,嵌套在JVM内部;

    它用来加载Java的核心类库(JAVA_HOME/jre/lib/rt.jar;resource.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类;

    不继承自java.lang.ClassLoader,没有父加载器;

    加载扩展类(ExtClassLoader)和应用扩展类加载器(AppClassLoader),并指定为它们的父类加载器;

    出于安全考虑,Bootstrap ClassLoader(启动类加载器)只加载包名为java,javax,sum等开头的类;

验证代码:

import sun.misc.Launcher;
import java.net.URL;

public class BootstrapClassLoaderDemo {
    public static void main(String[] args) {
        System.out.println("获取启动类加载器能够加载的api路径:");
        URL[] urls= Launcher.getBootstrapClassPath().getURLs();
        for (URL element:urls){
            System.out.println(element.toExternalForm());
        }
    }
}

运行结果:

获取启动类加载器能够加载的api路径:
file:/D:/JDK1.8/jre/lib/resources.jar
file:/D:/JDK1.8/jre/lib/rt.jar
file:/D:/JDK1.8/jre/lib/sunrsasign.jar
file:/D:/JDK1.8/jre/lib/jsse.jar
file:/D:/JDK1.8/jre/lib/jce.jar
file:/D:/JDK1.8/jre/lib/charsets.jar
file:/D:/JDK1.8/jre/lib/jfr.jar
file:/D:/JDK1.8/jre/classes
  • 扩展类加载器Extension ClassLoader

    java语言编写,由sum.misc.Launcher$ExtClassLoader实现;

    派生于ClassLoader类;

    父类加载器为启动类加载器(引导类加载器,Bootstrap ClassLoader);

    从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)
    下加载类库.如果用户创建的jar放在此目录,也会自动由扩展类加载器加载;

public class ExtensionClassLoaderDemo {
    public static void main(String[] args) {
        System.out.println("获取扩展类加载器能够加载的api路径:");
        // 获取java.ext.dirs的系统属性
        String extDirs=System.getProperty("java.ext.dirs");
        for (String element:extDirs.split(";")){
            System.out.println(element);
        }
    }
}

运行结果:

获取扩展类加载器能够加载的api路径:
D:\JDK1.8\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
  • 应用程序类加载器系统类加载器,AppClassLoader

    java语言编写,由sum.misc.Launcher$AppClassLoader实现;

    派生于ClassLoader类;

    父类加载器为扩展类加载器;

    它负责加载环境变量classpath或者系统属性:java.class.path指定路径下的类库;

    该类加载是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载;

    通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器;

  • 用户自定义加载器User-Defined ClassLoader

    在java的日常开发应用程序中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,来定值类的加载方式.

    为什么需要自定义类加载器?

    隔离加载类;

    修改类加载的方式;

    扩展加载源;

    防止源码泄露;(预防反编译篡改等,需要加密,需要自己定义类加载器来加载)

    用户自定义类加载器实现步骤:

  • 开发人员可以通过继承抽象类java.lang.ClassLoader类的方式,实现自己的类加载器,以满足一些特殊需求;

  • 在JDK1.2之前,在自定义类加载器时,总会去继承Class Loader类并重写LoadClass()方法,从而实现自定义的类加载器,但在1.2之后已不再建议用户去覆盖loadClass()方法,而是建议把自定义的类加载逻辑写在findClass()方法中;

  • 在编辑自定义类加载器时,如果没有太过于复杂的需求,而可以直接继承URLClassLoader类,这样就可以避免自己去编写findClass()方法及其获取字节码流的方式,使自定义类加载器编写更加简洁;

实现自定义类加载器的代码框架,如下:

import java.io.FileNotFoundException;
/**
 * @ClassName CustomClassLoader
 * @Description 自定义用户类加载器
 * @author taemin
 * @date 2020-05-02 12:18
 */
public class CustomClassLoader extends ClassLoader{
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException{
        try {
            byte[] result=getClassFromCustomPath(name);
            if (result==null){
                throw new FileNotFoundException();
            }else {
                return defineClass(name,result,0,result.length);
            }
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }
        throw new ClassNotFoundException(name);
    }

    private byte[] getClassFromCustomPath(String name) {
        //自定义路径中加载指定类:细节略
        //如果指定路径的字节码文件进行了加密,则需要在此方法中进行解密操作
        return null;
    }

    public static void main(String[] args) {
        CustomClassLoader customClassLoader=new CustomClassLoader();
        try{
            Class<?> clazz=Class.forName("One",true,customClassLoader);
            Object obj=clazz.newInstance();
            System.out.println(obj.getClass().getClassLoader());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

获取ClassLoader的途径:

  • 获取当前类的ClassLoader

    如:类名.class.getClassLoader()

  • 获取当前线程上下文的ClassLoader

    如:Thread.currentThread().getContextClassLoader()

  • 获取系统的ClassLoader

    ClassLoader.getSystemClassLoader()

  • 获取调用者的CLassLoader

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值