一、类加载的概念:
指的是JAVA进程运行时,要把.class文件从硬盘读取到内存,进行一系列校验解析的过程
也就是把 .class文件 转变成 类对象
硬盘 “转到” 内存
二、类加载的过程:
大致过程分为五个步骤:
1.加载
找到硬盘中.class文件 并打开
读取文件中的内容(二进制数据)
2.验证
确保读到的文件内容是合法的.class文件(字节码文件)
验证依据就是:JAVA8虚拟机文档规范,以这个文档里的格式为规范
左边这一列是数据的类型
JVM是用C++写的
所以这里4/2个字节无符号整数不一定代表的是Int和short
C++只规定了int 和 short 长度不短于多少
没规定是多少
u4:无符号4个字节的整数
u2:无符号2个字节的整数
右边这一列是变量的名字
magic:
也叫magic number 魔幻数字
应用于二进制文件格式当中
用来标识当前二进制文件属于哪种类型
因为二进制文件有很多种,图片是二进制,MP3,MP4等等都是二进制
所以不同格式的二进制文件,解析的方式不同,就需要通过magic来区分
minor_version 次版本
major_version 主版本
通常我们在讨论Java版本时,一般都是说Java8,9等等
在jvm内部开发其实还有一套版本
在jvm执行.clss文件时就会验证版本是否符合要求
一般来说高版本的jvm可以兼容低版本的.class文件
反之,很可能会出现问题
constant_pool
常量池
access_flags
针对于类的类型
public 或者 default
this_class
super_class
描述了这个类的其他信息以及父类信息
interfaces_count
表示当前类的接口信息
fields_count: field类型的数组
field_info一种结构体:包含了一些成员的信息,成员属性的名字,类型,访问权限等等
methods_count
方法信息
attributes
注解
3.准备
给类对象申请内存空间
此时申请到的内存空间,里面的默认值是全零的
相当于,此时类对象里的静态成员变量的值也是0
4.解析
主要是针对类中的字符串常量进行处理
JVM将常量池中的符号引用替换为直接引用(初始化常量)
符号引用:
比如有一个类
其中有一个属性 private String S = “hello”;
那么在.class文件中就会把hello存在constant_pool常量池当中
而 S 这个属性一般来说存储的是hello这个常量的地址
但是,在.clss文件中,硬盘文件没有地址的概念
所以S只能存放一个偏移量来记录hello的位置
这个偏移量就是这里说的符号引用
接下来就是转为直接引用:
JVM把.class加载到内存当中后,hello 就有了地址
此时S的值就会替换成hello地址,也就是直接引用
5.初始化
针对类对象完成后续的初始化
执行静态代码块的逻辑
有可能触发父类的加载
双亲委派模型
描述了(加载环节中)如何找到.class文件的策略
JVM中类加载的操作有一个专门的模块,叫做“类加载器”ClassLoader
JVM中默认有3个类加载器,也可以自定义几个
类加载器的作用:
给他一个全限定类名(带有包名的类名)(Java.lang.String)
找到对应的.class文件
而三个不同的类加载器就是从不同的目录中去查找
内置默认的3个类加载器:
BootstrapClassLoader
负责查找标准库中的目录
ExtensionClassLoader
负责查找扩展库的目录
扩展库:除标准库之外,JVM的厂商会额外拓展的功能
ApplicationClassLoader
负责查找当前项目的代码目录
以及第三方库的目录
三个类加载器之间,存在“父子”关系(不是类的继承之间的父子关系)
而是更像“二叉树”,存在一个指针,指向父亲节点(类加载器)
BootstrapClassLoader
↑ parent
ExtensionClassLoader
↑ parent
ApplicationClassLoader
所谓的双亲委派模型就是描述了上述类加载器之间是如何配合完成工作的
实际情况是单亲
双亲委派模型的工作过程:
1.以ApplicationClassLoader作为入口开始工作
2.以ApplicationClassLoader,不会立即搜寻自己负责的目录范畴而是交给他的父亲
3.ExtensionClassLoader,不会立即搜寻自己负责的目录范畴而是交给他的父亲
4.BootstrapClassLoader,不会立即搜寻自己负责的目录范畴而是交给他的父亲
5.BootstrapClassLoader,发现自己的父亲节点为空,只能在自己负责的目录范畴进行查找
找到了:则整个查找的流程解释,开始剩下的加载,验证...等等操作
没找到:则返回交给他的孩子节点进行查找,直到最后一个节点没找到,同时也没有孩子节点了,则没有找到符合的.class文件