目录
一、作用
二、角色
三、类的加载过程
(一)加载
(二)链接
1、验证
2、准备
3、解析
(三)初始化
(四)补充说明
四、类加载器分类
(一)引导类加载器和自定义加载器
(二)类加载器的获取
(三)启动类加载器(bootstrap ClassLoader)
(四)扩展类加载器
(五)应用类加载器
(六)用户自定义加载器
(七)关于ClassLoader
(八)双亲委派
这次学习的是JVM类加载子系统
负责从文件系统或者网络中加载Class文件,Class文件开头有特定标识(魔数)
Classloader只负责class文件的加载,至于是否可运行,则由执行引擎决定
加载的类信息存放于称为方法区的内存空间,除了类信息,方法区还会存放运行时常量池信息,还可能包括字符串字面量和数字常量
加载刚好是加载过程的一个阶段,二者意思不能混淆
通过一个类的全限定名获取定义此类的二进制字节流
将这个字节流所代表的的静态存储结果转化为方法区的运行时数据结构
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口
链接阶段分为验证-准备-解析三个小阶段
目的:确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全
四种验证
① 文件格式验证
② 元数据验证
③ 字节码验证
④ 符号引用验证
① 为类变量分配内存,并且设置该类变量的初始值,即零值
② 不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显式初始化
③ 不会为实例变量分配初始化,类变量会分配在方法区中,实例变量会随着对象一起分配到Java堆中
下面演示前向引用问题:
演示如果没有静态类变量和静态代码块,也不会有clinit(这里使用的是jclasslib插件,ideal里面查找装一下就可以了)
加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的。
解析阶段不一定,在某些情况下可以在初始化阶段之后再开始,为了支持Java语言的运行时绑定特性(也称为动态绑定或晚期绑定)
Java虚拟机规范严格规定了,有且只有六种情况,必须立即对类进行初始化
除了以上几种情况,其他使用类的方式被看做是对类的被动使用,都不会导致类的初始化
概念上,程序员自定义的就叫自定义加载器。但是,Java虚拟机规范定义的是:将所有派生于抽象类ClassLoader的类加载器都划分为自定义加载器(即可以将启动类加载器,应用类加载器和自定义加载器统称伟自定义加载器)
对于用户自定义类来说,默认使用系统类加载器进行加载
Java的核心类库,使用引导类加载器进行加载
public class ClassLoaderTest {
public static void main(String[] args) {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
// 获取其上层:扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1b6d3586
// 获取其上层:引导类加载器,发现获取不到
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null
// 对于用户自定义类来说,默认使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2
// 对于Java核心类库,都是使用引导类加载器进行加载的
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1); //null
}
}
启动类加载器又叫引导类加载器
应用类加载器又叫系统类加载器
为什么要用自定义类加载器
实现步骤:
是一个抽象类,除了启动类加载器,其他类加载器都继承自他
Java虚拟机对Class文件采用的是按需加载,而且加载class文件时,Java虚拟机使用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式
流程:
1、如果一个类加载器收到了类加载请求,它并不会自己先去加载。而是把这个请求委托给父类的加载器去执行
2、如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将达到顶层的启动类加载器
3、如果父类的加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
八字总结:向上委托,向下加载
优势:① 避免类的重复加载;② 保护程序安全,防止核心API被篡改
补充:
① 沙箱安全机制
我们自定义String类,但是在加载String类的时候会率先使用引导类加载器加载(因为向上委托,引导类加载到了直接就返回了),然后自定义String运行会报错没有main方法,就是因为加载的是rt.jar包中的String类,这样可以保证对Java核心源代码的保护,这就是沙箱安全机制
② 在JVM中表示两个class对象,是否为同一个类存在两个必要条件
- 类的完整类名必须一致,包括包名
- 加载这个类的ClassLoader必须相同
JVM必须知道一个类型是由启动类加载器加载的,还是由用户类加载器加载的。如果是用户类加载器加载的,JVM会将这个类加载器的一个引用作为类型信息的一部分,保存到方法区中。