JVM_01 类加载子系统

JVM 架构图

170ec7cd31581d15.webp

1.类加载子系统作用

QQ 截图 20200930113542.png

1.1 类加载器 ClassLoader 角色

QQ 截图 20200930113907.png

1.2 加载

170ec7daeca85a52.png

1.3 链接

1.3.1 验证:

1.3.2 准备:

1.3.3 解析:

1.4 初始化

170ec7ee5e625699.png

170ec7f508f951e6.png

170ec7fc6f50f714.png

170ec833466b3af3.png

170ec8415e75466b.png

2.类加载器分类

170ec88cffd157f0.png

2.1 自定义类与核心类库的加载器

/**
 * ClassLoader加载
 */
public class ClassLoaderTest {
    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@610455d6

        //获取其上层 获取不到引导类加载器
        ClassLoader bootStrapClassLoader = extClassLoader.getParent();
        System.out.println(bootStrapClassLoader);//null

        //对于用户自定义类来说:使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //String 类使用引导类加载器进行加载的  -->java核心类库都是使用引导类加载器加载的
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1);//null

    }
}

2.2 虚拟机自带的加载器

/**
 * 虚拟机自带加载器
 */
public class ClassLoaderTest1 {
    public static void main(String[] args) {
        System.out.println("********启动类加载器*********");
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        //获取BootStrapClassLoader能够加载的api路径
        for (URL e:urls){
            System.out.println(e.toExternalForm());
        }

        //从上面的路径中随意选择一个类 看看他的类加载器是什么
        //Provider位于 /jdk1.8.0_171.jdk/Contents/Home/jre/lib/jsse.jar 下,引导类加载器加载它
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println(classLoader);//null

        System.out.println("********拓展类加载器********");
        String extDirs = System.getProperty("java.ext.dirs");
        for (String path : extDirs.split(";")){
            System.out.println(path);
        }
        //从上面的路径中随意选择一个类 看看他的类加载器是什么:拓展类加载器
        ClassLoader classLoader1 = CurveDB.class.getClassLoader();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@4dc63996
    }
}

2.3 用户自定义类加载器

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

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

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

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

3 ClassLoader 的常用方法及获取方法

3.1 ClassLoader 类,它是一个抽象类,其后所有的类加载器都继承自 ClassLoader(不包括启动类加载器)

方法名称 描述
getParent() 返回该类加载器的超类加载器
loadClass(String name) 加载名称为 name 的类,返回结果为 java.lang.Class 类的实例
findClass(String name) 查找名称为 name 的类,返回结果为 java.lang.Class 类的实例
findLoadedClass(String name) 查找名称为 name 的已经被加载过的类,返回结果为 java.lang.Class 类的实例
defineClass(String name,byte[] b,int off,int len) 把字节数组 b 中的内容转换为一个 Java 类 ,返回结果为 java.lang.Class 类的实例
resolveClass(Class<?> c) 连接指定的一个 Java 类

3.2 ClassLoader 继承关系

拓展类加载器和系统类加载器间接继承于 ClassLoader 抽象类

170ec8a4eaae0b43.png

3.3 获取 ClassLoader 的途径

  1. 获取当前类的 ClassLoader:class.getClassLoader()
  2. 获取当前线程上下文的 ClassLoader:Thread. currentThread() . getContextClassLoader ()
  3. 获取系统的 ClassLoader:ClassLoader . getSystemClassLoader ()
  4. 获取调用者的 ClassLoader:Drive rManager . getCallerClassLoader ()

QQ 截图 20200930135852.png

4. 双亲委派机制

Java 虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的 class 文件加载到内存生成的 class 对象。而且加载某个类的 class 文件时,Java 虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式

4.1 双亲委派机制工作原理

170ec8cbbe16af0c.png

如图,虽然我们自定义了一个 java.lang 包下的 String 尝试覆盖核心类库中的 String,但是由于双亲委派机制,启动加载器会加载 Java 核心类库的 String 类(BootStrap 启动类加载器只加载包名为 Java、javax、sun 等开头的类),而核心类库中的 String 并没有 main 方法

170ec8d5358f0991.png

4.2 双亲委派机制的优势

170ec8ddb41c5559.png

QQ 截图 20200930141305.png

5. 沙箱安全机制

自定义 String 类,但是在加载自定义 String 类的时候回率先使用引导类加载器加载,而引导类加载器在加载过程中会先加载 JDK 自带的文件(rt.jar 包中的 java\lang\String.class),报错信息说没有 main 方法就是因为加载的是 rt.jar 包中的 String 类。这样可以保证对 Java 核心源代码的保护,这就是 沙箱安全机制

6.其他

对类加载器的引用

JVM 必须知道一个类型是有启动类加载器加载的还是由用户类加载器加载的。如果一个类型由用户类加载器加载的,那么 jvm 会将这个类加载器的一个引用作为类型信息的会议部分保存在方法区中。当解析一个类型到另一个类型的引用的时候,JVM 需要保证两个类型的加载器是相同的。

类的主动使用和被动使用

Java 程序对类的使用方式分为:主动使用和被动使用

  • 后端
    37 引用 • 115 回帖 • 1 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    2812 引用 • 8043 回帖 • 749 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    152 引用 • 115 回帖 • 3 关注
1 操作
wlgzs-sjl 在 2020-10-05 22:32:03 更新了该帖

赞助商 我要投放

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...