【简单说】类加载过程

JVM是什么

java从编码到执行的过程:
java文件通过javac命令转换为.class文件,然后会被classLoader转载到内存中,可能.class文件还包含某些java类库,类库也会被转载到内存中,转载完之后就会通过字节码解释器或者JIT即时编译器进行解析,最后通过执行引擎进行运行。
在这里插入图片描述
JVM与JAVA无关:
任何语言只要能编译成class文件,它都能在JVM上运行

JDK、JRE、JVM的关系

在这里插入图片描述

类加载的三个过程

类加载的过程分为三部分:
1、加载过程
2、连接过程
3、初始化过程
在这里插入图片描述

加载过程采用的是双亲委派机制

双亲委派机制实际上是在ClassLoader中的loadClass方法中实现的
要了解双亲委派机制,首先要了解类加载器的关系:
在这里插入图片描述
解释:
CustomClassLoader的父加载器是APPClassLoader,
APPClassLoader的父加载器是ExtensionClassLoader,
ExtensionClassLoader的父加载器是BootstrapLoader


加载过程的双亲委派解释:

第一步在缓存中找需要加载的文件
.class文件通过custom.loadClass加载进去后,现在Custom加载器的缓存中找,找着则返回结果,没有找着,到父加载器的缓存进行查找,以此类推,最后到Bootstrap加载器中进行查找,如果还是不能在缓存中找到,那么就执行第二步
在这里插入图片描述
第二步:通过每个类加载器执行不同的功能,对.class文件进行加载
每一次父加载器找不到加载的文件就到子加载器中进行查找,以此类推
在这里插入图片描述

为什么使用双亲委派机制进行加载

使用双亲委派机制很大程度上保证了安全,每次加载的过程中,最终都会被Bootstrap ClassLoader所响应,加载的Object类也只会出现一个,基于这种机制,最后才会是自定义的ClassLoader,所以用户自定义的类不会影响原来的类。


打破双亲委派机制问题

在什么情况下打破双亲委派机制:
1、JDK1.2之前,自定义的ClassLoader都必须重写loadClass()
2、ThreadContextClassLoader(线程上下文类加载器)可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定,经典案例就是jdbc
3、热启动热部署,像tomcat,osgi都有自己的模块指定classLoader,如果一个项目中有不同版本的jar包,按照双亲委派机制处理的话,只会加载第一个jar包,后面的jar包因为同名,不会被加载进去,但是像tomcat这种的是可以加载同一类库的不同版本

怎么打破双亲委派机制:重写loadClass()方法即可

类加载的连接过程(Linking)

1、Verification校验过程

  • 验证文件是否符合JVM规定

2、Preparation准备过程

  • 静态成员变量赋予默认值

3、Resolution解析过程

  • 将类、方法、属性等符号引用解析为直接引用,常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

类加载的初始化过程(Initializing)

给静态成员变量赋初始值

public class Test {

    public static void main(String[] args) {
        System.out.println(P.num);
    }
}

class P{
    public static int num = 10;
    public static P p = new P();

    public P() {
        num++;
    }
}

输出结果:
在这里插入图片描述

public class Test {

    public static void main(String[] args) {
        System.out.println(P.num);
    }
}

class P{
    public static P p = new P();
    public static int num = 10;

    public P() {
        num++;
    }
}

输出结果:
在这里插入图片描述

解释:第一段代码执行的时候,调用P.num的时候,会先把P类加载到内存中,先进行Verification校验,然后进行Preparation把num的值赋值为0,还把p这个属性进行赋值,p在此时属于空值,所以不执行num++,然后进行Resolution,再进行Initializing赋予初始化,首先初始num,num变成10,然后初始p,执行num++,所以打印出来的num为11。
第二段代码,调用P.num的时候,会先把P类加载到内存中,先进行Verification校验,然后进行Preparation把num的值赋值为0,还把p这个属性进行赋值,p在此时属于空值,所以不执行num++,然后进行Resolution,再进行Initializing赋予初始化,首先初始p,执行num++,此时num加1前为0,执行完后num为1,再初始化num,num就直接赋值为10了
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值