类加载器
将class文件加载进jvm的方法去,并在方法去中创建一个java.lang.Class对象作为外界访问这个类的接口。实现这个动作的代码模块称为类加载器。
类加载器分类
- 启动类加载器(Bootstrap ClassLoader)
- 扩展类加载器
- 应用程序类加载器
- 自定义加载器
启动类加载器
它是Java加载系统的根部,它负责加载Java核心类库(JAVA_HOME/jre/lib/rt.jar中的类库),由于启动类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不同于其他的类加载器,启动类加载器并不继承自java.lang.ClassLoader类。
扩展类加载器
这是一个由Sun的Extension ClassLoader实现的,它负责加载Java的扩展库
(JAVA_HOME/jre/lib/ext或者由系统变量java.ext.dirs指定路径中的类库)。
开发者可以直接使用标准扩展目录(ext目录)做类库的扩展。
应用程序类加载器
这是ClassLoader中getSystemClassLoader()方法的返回值,所以也称为系统类加载器。
它负责加载环境变量CLASSPATH或者-classpath/-cp命令行选项指定路径中的类库。该类加载器是用户自定义加载器的默认父加载器。
自定义加载器
自定义类加载器(Custom ClassLoader):Java允许我们自定义类加载器,只需要继承java.lang.ClassLoader类,并重写findClass方法即可。
这样可以让Java类具有动态加载和热加载的能力,提高了Java程序的灵活性。
类加载过程
- 加载:
- 连接:
2.1. 验证
2.2. 准备
2.3. 解析 - 初始化
加载:
- 加载是指Java虚拟机查找字节流(查找.class文件),并且根据字节流创建java.lang.class对象的过程。
- 这个过程,将类的.class文件中的二进制数据读入内存,凡在运行时数据区的方法区内。然后就在堆中创建java.lang.class对象,用来封装类在方法去中的数据结构
- 类加载阶段
- (1) Java虚拟机将.class文件读入内存,并为之创建一个Class对象。
- (2)任何类被使用时系统都会为其创建一个且仅有一个Class对象。
- (3)这个Class对象描述了这个类创建出来的对象的所有信息,比如有哪些构造方法,都有哪些成员方法,都有哪些成员变量等。
连接:
验证
- 验证阶段作用是保住Class文件的字节流包含的信息符合JVM规范,不会给JVM造成危害。如果验证失败,就会抛出一个java.lang.VerifyError异常或其子类异常。验证过程分为四个阶段:
- (1)文件格式验证:验证字节流文件是否符合Class文件格式规范,并且能被当前虚拟机正确处理。
- (2)元数据验证:是对字节码描述信息惊喜语义分析,以保证其描述的信息符合java语言规范要求
- (3)字节码验证:主要是惊喜数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机
- (4)符号引用验证:符号引用验证发生在虚拟机将符号转化为直接引用的时候,这个转化动作将在解析阶段发生。
准备
- 准备阶段为变量分配内存并设置类变量的初始化。这个阶段分配的仅为类的变量,而不包括类的实例变量。对于非final的变量,JVM会将其设置成“零值”,而不是赋值语句的:private static int size = 12 。那么在这个阶段,size的值为0,而不是12.但非final修饰的类变量将会赋值成真实的值。
解析
- 解析过程是将常量池内的符号引用替换成直接引用,主要包括四类型引用解析。类或接口的解析,字段解析、方法解析、接口方法解析。
初始化:
双亲委派模型
如果一个类接受到请求,他首先将请求交给父类加载器,直至找到启动类加载器,如果父类加载器加载失败,当前类加载器才会自己加载类。
为什么要使用双亲委派模型:防止类重复加载,保护核心的类