jvm类加载过程

类的生命周期

在这里插入图片描述

加载

加载主要做三件事:

找到类文件(通过类的全限定名来获取定义此类的二进制字节流)
放入方法区(将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构)
开个入口(生成一个代表此类的java.lang.Class对象,作为访问方法区这些数据结构的入口)
总的来讲,这一步就是通过类加载器把类读入内存。需要注意的是,第三步虽然生成了对象,但并不在堆里,而是在方法区里

连接

校验
顾名思义,检查Class文件的字节流中包含的信息是否符合当前虚拟机的要求

  • 文件格式验证:CA FE BA BE(魔数,Java虚拟机识别);主次版本号;常量池的常量中是否有不被支持的常量类型;指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量

  • 元数据验证:对字节码描述的信息进行语义分析,保证描述符合Java规范;类是否有父类,除了Object之外,所有的类都应该有父类;类的父类是否继承了不允许被继承的类(被final修饰的类);如果这个类不是 抽象类,是否实现了其父类或接口中要求实现的所有方法。;类的字段,方法是否与父类的产生矛盾。例如方法参数都一样,返回值不同

  • 字节码验证:通过数据流分析和控制流分析,确定程序语义是合法的,符合逻辑的。;对类的方法体,进行校验分析,保证在运行时不会做出危害虚拟机的行为;保证任意时刻操作数栈的数据类型与指令代码序列都能配合工作,不会出现类似于在操作数栈放了一个int类型的数据,使用时却按照long类型加载到本地变量表中的情况。;保障任何跳转指令都不会跳转到方法体之外的字节码指令上。

  • 符号引用验证:通过字符串描述的全限定名是否能找到对应的类;符号引用中的类、字段、方法的可访问性是否可被当前类访问
    准备

  • 为类变量分配内存,并且设置该类变量的初始值,即零值(public static int value = 0);不包含用final修饰的static,因为final在编译的时候就会分配内存了,准备阶段会显示初始化(public static final int value = 123;只显示初始化,不会分配内存)。不会为实例变量分配初始化,类变量会分配在方法区中,实例变量会随着对象一起分配到Java堆中

解析
解析阶段就是jvm将常量池的符号引用替换为直接引用。

恩…啥是常量池?啥是符号引用?啥是直接引用?

常量池我们放在jvm内存结构里说。先来说下什么是符号引用和直接引用。

符号引用和直接引用
假设有一个Worker类,包含了一个Car类的run()方法,像下面这样:

class Worker{
    ......
    public void gotoWork(){
    	car.run(); //这段代码在Worker类中的二进制表示为符号引用        
	}
    ......
}

在解析阶段之前,Worker类并不知道car.run()这个方法内存的什么地方,于是只能用一个字符串来表示这个方法。该字符串包含了足够的信息,比如类的信息,方法名,方法参数等,以供实际使用时可以找到相应的位置。

这个字符串就被称为符号引用。

在解析阶段,jvm根据字符串的内容找到内存区域中相应的地址,然后把符号引用替换成直接指向目标的指针、句柄、偏移量等,这之后就可以直接使用了。

这些直接指向目标的指针、句柄、偏移量就被成为直接引用。

初始化
类的初始化的主要工作是为静态变量赋程序设定的初值。
如上面的变量:

public static int value = 123;

在这里给他赋初始值。

类加载器

jvm自带三种类加载器,分别是:

  1. 启动类加载器。
  2. 扩展类加载器。
  3. 应用程序类加载器
    他们的继承关系如下:
    在这里插入图片描述
    双亲委派机制

双亲委派机制工作过程如下:

  1. 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
  2. 当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.
  3. 当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回

双亲委派机制优点:

  1. 避免重复加载。当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
  2. 为了安全。避免核心类,比如String被替换。

打破双亲委派
“双亲委派”机制只是Java推荐的机制,并不是强制的机制。

比如JDBC就打破了双亲委派机制。它通过Thread.currentThread().getContextClassLoader()得到线程上下文加载器来加载Driver实现类,从而打破了双亲委派机制。

参考文章:https://www.cnblogs.com/bailiyi/p/11887470.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值