首页 > 其他分享 >类加载子系统

类加载子系统

时间:2023-12-13 19:00:13浏览次数:25  
标签:System 子系统 ClassLoaderDemo println out public 加载

类加载子系统负责从文件系统或者网络中加载字节码文件

类加载子系统整体架构图

可以看出,整个类加载子系统分为三个部分,加载、链接、初始化

一、加载

JVM 支持两种类型的类加载器,分别是引导类加载器(BootStrapClassLoader)和自定义加载器

从概念上来讲,自定义加载器一般指的是程序中由开发人员自定义的一类加载器,但是 JVM 规范中却不是这么定义的,而是将所有派生于抽象类 ClassLoader 的类加载器都划分为自定义类加载器

无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有 3 个

BootStrapClassLoader、ExtentionClassLoader、ApplicationClassLoader

public class ClassLoaderDemo {
    public static void main(String[] args) {
        // 获取用户自定义类加载器
        ClassLoader applicationClassLoader = ClassLoaderDemo.class.getClassLoader();
        System.out.println(applicationClassLoader);

        // 获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 获取系统类加载器的上层加载器(扩展类加载器)
        ClassLoader extentionClassLoader = systemClassLoader.getParent();
        System.out.println(extentionClassLoader);

        // 获取扩展类加载器的上层加载器(引导类加载器)
        ClassLoader bootStrapClassLoader = extentionClassLoader.getParent();
        System.out.println(bootStrapClassLoader);
    }
}

从打印结果可知,用户自定义类所使用的加载器就是系统类加载器 AppClassLoader

系统类加载器的上层加载器是 ExtClassLoader

由于 BootStrapClassLoader 不是 Java 语言实现的,所以这里输出为 null

BootStrapClassLoader
BootStrapClassLoader 称为启动类加载器或者引导类加载器,这个类加载器使用 C/C++ 实现,嵌套在 JVM 内部,它用来加载 Java 的核心类库
${JAVA_HOME/jre/lib/rt.jar
${JAVA_HOME/jre/lib/resources.jar
sun.boot.class.path 路径下的 jar 包
出于安全考虑,启动类加载器只加载包名为 java、javax、sun 等开头的类

ExtentionClassLoader
该加载器由 Java 语言编写,从 java.ext.dirs 系统属性所指定的目录中加载类,或从 ${JAVA_HOME}/jre/lib/ext 加载类,如果用户创建的 jar 存放在此目录下,也会自动由扩展类加载器进行加载

ApplicationClassLoader
该加载器由 Java 语言编写,它负责加载 classpath 下的类库,它是程序中默认的类加载器,一般来说,我们编写的类都是由它来完成加载的

public class ClassLoaderDemo {
    public static void main(String[] args) {
        System.out.println("系统类加载器加载路径");
        URL[] bootStrapUrls = Launcher.getBootstrapClassPath().getURLs();
        for (URL urL : bootStrapUrls) {
            System.out.println(urL.toExternalForm());
        }
        System.out.println("扩展类加载器加载路径");
        String extDirs = System.getProperty("java.ext.dirs");
        for (String extPath : extDirs.split(",")) {
            System.out.println(extPath);
        }
    }
}

双亲委派机制

如果一个类加载器收到类加载请求,它并不会自己先去加载,而是把这个请求委托给上级的加载器去执行加载任务,如果它的上级加载器还有上级,那么进一步向上委托,直至达到最顶层的 BootStrapClassLoader,如果上级类加载器可以完成加载任务就直接成功返回,如果上级类加载器无法完成加载任务,下级加载器才会尝试自己去加载,这就是双亲委派模式

例如: 自定义一个类 ClassLoaderDemo,我要去使用这个类就必然要先去加载它,通过上帝视角我们知道这个类是通过 AppClassLoader 去加载的,但是实际上 AppClassLoader 并不会先去加载 ClassLoaderDemo 这个类,而是委托给上级的 ExtClassLoader 去加载,而 ExtClassLoader 还有上级 BootStrapClassLoader,那么继续向上委托直到 BootStrapClassLoader 位置,由于 BootStrapClassLoader  加载不到 ClassLoaderDemo 这个类,那么 EExtClassLoader 就尝试去加载,而 ExtClassLoader 也加载不要,AppClassLoader 才尝试去加载

双亲委派机制的引入是为了 避免类的重复加载,防止恶意篡改核心 API,保护程序安全

二、链接

链接阶段又分为三个小阶段
验证阶段
确保字节码文件的字节流符合当前 JVM 规范,保证被加载的类的正确性,不会危害虚拟机自身安全
主要包括四种验证:文件格式验证、元数据验证、字节码验证、符号引用验证

准备阶段
为类变量分配内存空间并设置默认初始值
注意这里不包含 final 修饰的类变量,因为 final 修饰的类变量在编译期就确定了

解析阶段
将常量池内的符号引用转换为直接引用

三、初始化

初始化阶段就是执行 <clinit>() 方法的过程,此方法不需要定义,是 javac 编译器自动收集类中的所有类变量的赋值动作和静态代码块的语句合并而来的

<clinit()>() 不同于类的构造器,类构造器在 JVM 视角下对应的是 <init>() 方法

若该类已经具有父类,JVM 会保证子类的 <clinit>() 执行前,父类的 <clinit>() 已经执行完毕

虚拟机必须保证一个类的 <clinit>() 方法在多线程下被同步加锁

示例代码

public class ClassLoaderDemo {
    static {
        int a = 10;
    }

    public static int b = 20;

    public static int c = 30;

    static {
        int d = 40;
    }
}

根据上图可知,一个 ClassLoaderDemo 中的所有静态变量和静态代码块都被 java 编译器收集起来了,收集到了一个 <clinit>() 方法中,代码中静态变量和静态代码块的书写顺序就是实际的收集顺序

ps: <init>() 方法对应的是类的构造器、构造代码块...

上面代码中的 public static int b = 20 整个初始化过程如下

首先在链接的准备阶段进行默认初始化 (b = 0),然后在初始化阶段才完成显示初始化 (b = 20)

 

标签:System,子系统,ClassLoaderDemo,println,out,public,加载
From: https://www.cnblogs.com/xiaomaomao/p/17899726.html

相关文章

  • 4412 设备树 没有eth0 没有加载 dm9621 驱动。
    问题: 在4412的板卡上烧写完,设备树的镜像之后,系统启动之后,发现没有网络。 这种情况,在从新烧写一遍镜像就可以了,具体原因不清楚,可能跟设备树的uboot的烧写命令有关。  总结:4412 8G以及16Gemmc的核心板在设备树的镜像上网络上都是可以的,主要就是需要多烧......
  • UBUNTU 18.04.6 的Quartus里面转换sof到rbf文件在uboot阶段加载时出错或者在kernel启
    参考Intel的SD卡image设计的教程(https://rocketboards.org/foswiki/Documentation/EmbeddedLinuxBeginnerSGuide) 确认DE10-Nano的MSEL设置为01010,插上SD卡 给DE10-Nano上电,发现可以启动,但卡死在这里不动了: 如果只测试Preloader和uboot的时候也有这个错误: ......
  • JVM虚拟机系统性学习-类加载子系统
    类加载子系统JVM架构如下图,接下来将从类加载子系统、运行时数据区来逐步讲解JVM虚拟机类加载的时机类加载的时机主要有4个:遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果对应的类没有初始化,则要先进行初始化new关键字创建对象时读取或设置一个类型的......
  • PCI总线子系统 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/PCI/index.htmlPCI总线子系统如何编写LinuxPCI驱动程序1.1.PCI驱动程序的结构1.2.pci_register_driver()调用1.3.如何手动查找PCI设备1.4.设备初始化步骤1.5.PCI设备关闭1.6.如何访问PCI配置空间1.7.其他有趣的函数1.......
  • 项目开发之vue中如何刷新子组件,重置组件,重新加载子组件
    项目场景:在父组件循环数据列表中,列表中有事件点击打开某个子组件;父组件中要更新子组件的值问题描述在父组件循环数据列表中,点击打开某个子组件,A数据打开子组件后,B数据再打开子组件,此时子组件中会有缓存A数据。原因分析:子组件没有刷新,还是停留在上一个数据缓存解决方案:给组件添......
  • vant列表下拉刷新、上拉加载|list使用方法
    Vant列表list使用方法Vant列表list的使用方法和注意事项下列代码为允许下拉刷新1.使用组件<van-pull-refreshv-model="refreshing"@refresh="onRefresh"><van-listv-model="loading":finished="finished"......
  • 炫酷CSS加载动画
    HTML结构首先是HTML代码,定义了一个类名container的<div>容器:在这个容器里面包含了一些加载器.loader,每个加载器都具有不同的旋转角度自定义属性--r(1~4),而每个加载器里面有20个<span>元素,并且也都具有不同的旋转角度自定义属性--r(1~20)。后面会通过CSS设置不同的旋转角度属性--r和......
  • Cesium 加载倾斜摄影模型记录(osgb切片,shp拔高切片、模型加载、鼠标移入选中、点选查
    一、shp模型拔高切片shp如果数据量过大,做分类处理,加载会异常慢,所以需要先对其进行分割之后再进行切片(用qgis即可)切片规则设置1、记得勾选构造底面 2、如果你的shp数据中有高度字段的话,可以选择高度字段,如果没有的话,设置固定高度的高度比你的模型稍微高一点,可以保证包着整个模型,......
  • GPIO子系统驱动程序 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/driver-api/gpio/drivers-on-gpio.htmlGPIO子系统驱动程序在Linux内核中,有许多标准的GPIO任务的驱动程序,它们提供了适当的内核空间和用户空间的API/ABI,并且可以通过硬件描述(如设备树或ACPI)与其他内核子系统轻松连接:leds-gpio:driver......
  • PINCTRL(PIN CONTROL)子系统 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/driver-api/pin-control.htmlPINCTRL(PINCONTROL)子系统本文概述了Linux中的pincontrol子系统。该子系统涉及以下内容:枚举和命名可控制的引脚引脚、Pads、Fingers(等)的复用,请参见下文了解详情配置引脚、Pads、Fingers(等),例如软件控制......