classloader的主要职责就是负责加载各种class文件到jvm中,classloader是一个抽象的class,给定一个class的二进制文件名,classloader会尝试加载并且在JVM中生成构成这个类的各个数据结构,然后使其分布在JVM对应的内存区域中.
分为三个比较大的阶段,分别是加载阶段,连接阶段和初始化阶段.
加载阶段:主要负责查找并且加载类的二进制数据文件,其实就是class文件
连接阶段:
分为三个阶段
验证:主要是确保类文件的正确性,比如class版本,class文件的魔术因子是否正确.
准备:为类的静态变量分配内存,并且为其初始化默认值.
解析:把类中的符号引用转换为直接引用
初始化阶段:为类的静态变量赋予正确的初始值.
JVM对类的初始化是一个延迟的机制,即使用的是lazy的方式,当一个类在首次使用的时候才会被初始化,在同一个运行时包下,一个Class只会被初始化一次,在同一个运行包下,一个Class只会被初始化一次.
6种主动使用类的场景:
被动
类的加载阶段
类的加载同一个全限定名:通过包名+类名来获取二进制流
除此之外:运行时动态生成
通过网络获取
通过读取zip文件获取类的二进制字节流,比如jar,war
将类的二进制数据存储在数据库的BLOB字段类型
运行时生成class文件,并且动态加载
3.2类的连接阶段
验证 目的:确保class文件的字节流锁包含的内容符合当前的jvm的规范要求,并且不会出现危害jvm自身安全的代码,当字节流的信息不符合要求时,则会抛出VerifyError这样的一异常或者子异常
验证信息:
(1)验证文件格式:
验证文件头部的魔术因子,该因子决定了这个文件到底是什么类型,class文件的魔术因子是0XCAFEBFBE.
主次版本号,查看当前的class文件版本是否符合jdk所处理的范围.
构成文件的字节流是否存在残缺或者其他附加信息,主要查看class的MD5指纹.
常量池中的常量是否存在不被支持的变量类型,比如int64
指向常量中的引用收付知道了不存在的常量或者改常量的类型不被支持 .
(2)元数据验证
元数据的验证其实是对class的字节流进行语义分析的过程,整个语义分析就是为了确保class字节流符合JVM规范的要求.
@检查这个类是否存在父类,是否继承了某个接口,这些父类和接口是否合法,或者是否真实存在.
@检查改类收付继承了被final修饰的类,被final修饰的类是不允许继承并且期中的方法是不允许被override的
@检查该类是否为抽象类,如果不是抽象类,是否实现了父类的抽象方法或者接口中的所有方法
@检查方法重载的合法性,比如相同的方法名称,相同的参数,但是返回类型不同,这都是不被允许的
@其他语义验证
(3).符号引用验证
主要作用就是验证符号引用转换为直接引用时的合法性
通过符号引用描述的字符串全限定名称是否能够顺利的找到相关的类
符号引用中的类.字段,方法,收付对当前类可见,比如不能访问引用类的私有方法
其他
符号引用的验证目的是为了保证解析动作的顺利进行,比如,如果某个类的字段不存在,则会抛出NosuchfieldError,若该方法不存在时,则抛出nosuchmethoderroe等,我们在使用反射的时候也会遇到这样的异常信息.
2.准备
为对象的类变量也是就是静态变量,分配内存并且设置初始值了,类变量的内存会被分配到方法区内,不同实例变量会分配到堆内存中
所谓初始值,其实就是为相应的类变量给定一个相关类型在没有被设置值时的默认值.
3.解析
所谓解析就是在常量池中寻找类,接口,字段和方法的符号引用,并且将这些符号引用替换成直接引用的过程.
类接口解析
字段的解析
类方法的解析
接口方法的解析
类的初始化阶段:
class initialize:
这篇博客总结到这里,下篇博客来总结java几个类加载器.