02. 类加载机制

类加载机制

一个类从加载到使用,一般会经历下面这个过程

加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载

JVM在什么情况下会加载一个类

JVM在执行过程中,一般在什么情况下会加载一个类呢?也就是说,啥时候会从 *.class字节码文件中加载这个到内存中,其实就是在你代码中使用到这个类的时候。

比如,代码中包含"main()"方法的主类一定会在JVM进程启动之后被加载到内存,开始执行你的"main()"方法中的代码,接着遇到你使用到的其他类,此时就会从对应的 .class字节码文件加载到对应的类到内存里来

验证、准备和解析过程

所谓的验证阶段,就是根据Java虚拟机规范,来校验加载进来的 .class文件中的内容,是否符合指定的规范,必须完全符合JVM规范,后续才能交给JVM来运行,否则,是无法执行这个字节码的

所谓的准备阶段,先看下一段代码

public class Test{
    public static int a;
}

我们自己写的类,其实会有一些类变量,就如上面的Test类,其Test.class文件内容刚刚被加载到内存之后,会进行验证,确认这个字节码文件是规范的,接着会进行准备工作

就是给Test类分配一定的内存空间

然后给他里面的类变量(也就是static修饰的变量)分配内存空间,来一个默认的初始值

如上面的程序中,就会给a这个类变量分配内存空间,给一个0这个初始值

所谓的解析阶段,实际上是把符号引用替换成直接引用

核心阶段,初始化

在准备阶段,会为Test类给分配好内存空间,并给类变量a一个默认的初始值0,接下来,在初始化阶段,就会真正执行类初始化代码了

什么是类初始化代码呢,看下面代码

public class Test{
    public static int a = Configuration.getInt("test.a");
    public static Map<String, String> values;

    static{
        loadDataFromDB();
    }

    public static void loadDataFromDB(){
        this.value = new HashMap<>();
    }
}

对于a这个类变量,是通过 Configuration.getInt("test.a")来获取一个值,并赋值给a,那么在准备阶段会执行这个赋值逻辑么?

答案是否定的,在准备阶段,仅仅是给a这个变量开辟了一个内存空间,初始化为0而已

这段赋值代码在初始化阶段执行,在这个阶段,Configuration.getInt("test.a") 会完成一个配置项的读取,然后赋值给这个类变量a

再比如static静态代码块,也是在初始化阶段执行的,上面的代码,调用loadDataFromDB()方法从数据库获取数据,并且放到静态变量values中

以上就是类的初始化

那么什么时候会初始化一个类?有如下几种情况

包含"main()"方法的主类,必须是立马初始化的

new Test()来实例化类的对象,此时会触发类的加载到初始化的全过程,即把这个类准备好,然后再实例化一个对象出来

此外,如果初始化一个类的时候,发现他的父类还没初始化,那么必须先初始化他的父类

类加载器

java里面有哪些类加载器呢,一起看下

  1. 启动类加载器

    Bootstrap ClassLoader,他主要负责加载机器上安装的Java目录下的核心类,即JAVA_HOME/lib目录,该目录下是Java最核心的一些类库,支撑Java系统运行,一旦JVM启动,就会依托启动类加载器,去加载JAVA_HOME/lib目录下的核心类库

  2. 扩展类加载器

    Extension ClassLoader,JAVA_HOME/lib/ext目录,这里面也有一些类,就需要扩展类加载器来加载,支撑系统的运行,一旦JVM启动,就会依托扩展类加载器,去加载JAVA_HOME/lib/ext目录下的类

  3. 应用程序类加载器

    Application ClassLoader,这个类加载器负责去加载"ClassPath"环境变量所指定的路径中的类,也就是加载自己编写的Java代码,到内存里

  4. 自定义加载器

    可以自定义类加载器,根据需求加载类

双亲委派机制

JVM类加载器是有亲子层级结构的,就是说,启动器加载器是最上层的,扩展类加载器在第二层,第三层是应用程序类加载器,最后一层是自定义类加载器,如下图所示


13274599-8173e9facf5aa0fd.png
1. 双亲委派机制.png

基于上图的亲子层级结构,就有一个双亲委派的机制,什么是双亲委派机制呢,就是说

应用程序类加载器需要加载一个类,他首先会委派给自己的父类加载器去加载,最终传递到顶层的类加载器去加载,但是如果父类加载器在自己负责加载的范围内,没找到这个类,那么就会下推将加载圈给自己的子类下载器

用一个例子说明吧

比如现在JVM需要加载Test类,此时应用程序类加载器会问自己的爸爸,也就是扩展类加载器,你能加载这个类么?

扩展类加载器问自己的爸爸,启动类加载器,你能加载这个类么?

启动类加载器心想,我在Java安装目录下,没有找到这个类啊,自己找去吧

然后下推加载权利给扩展类加载器这个儿子,结果扩展类加载器找了下,也没找到这个类,就下推加载权利给应用程序类加载器

应用程序类加载器在自己负责的范围内,找到了这个类,就将这个类加载到内存里面去了

这就是双亲委派模型,先找父亲去加载,不行的话由儿子来加载

这样,可以避免多层级的加载器结构重复加载某些类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值