类加载过程:
类的加载过程包括加载、检验、准备、解析、初始化五个阶段。
1.加载
-
加载阶段是整个类加载过程中的第一个阶段,这个加载只是类加载阶段中的其中一个阶段。
-
虚拟机需完成以下三件事
- 通过一个类的全限定类名来获取此类二进制字节流(该过程没有指定该全限定类名位置)
- 将这个字节流中的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
-
非数组类型的加载阶段
- 加载阶段既可以使用java虚拟机内置的引导类加载器来完成加载,也可以使用自定义类加载器来完成。(继承Classloader,重新写finclass方法,在自定义类加载器中会说到)
-
数组类的加载过程
- 如果数组的组件类型是引用类型,则采用递归方式加载组件类型
- 如果数组的组件类型不是引用类型,虚拟机会把这个数组标记与引导类加载器关联,
- 数组类的可访问性与他的组件可访问性一致
- 数组组件类型(Componrnt Type):是指数组去掉一个维度的类型,比如int[]数组的组件类型是int。数组组件类型不是数组中元素的类型。
2.验证
- 验证阶段是来链接阶段的第一步,这个阶段的目的
- 确保Class文件中的字节流包含的信息符合规范。保证这些信息被当作运行后不会危害虚拟机自身安全。验证字节码是虚拟机自我保护的一项必备措施。
- 验证阶段分为4个阶段:文件格式验证、元数据验证、字节码验证、符号引用验证
- 文件验证:验证是否符合Class文件格式的规范(比如Class文件中的魔数、版本号等很多验证点),并且能被当前版本的虚拟机处理。这个阶段验证时基于二进制字节流进行的,只有通过这个阶段的验证后,这些字节流才能进入java虚拟机内存的方法区中进行存储。
- 元数据验证:对字节码描述的信息进行语义分析,保证描述的信息符合规范(是否有父类、父类是否被final修饰等很多验证点)。
- 字节码验证:整个验证阶段中最复杂的,主要目的是数据流分析和控制流分析。对类的方法体(Class文件中的Code属性)进行校验分析,保证被校验类的方法体在运行时不会做出危害虚拟机安全的行为。
- 符号引用验证:这一阶段验证行为发生在虚拟机将符号引用转化为直接引用的时候,这个动作将在链接的第三个阶段“解析”中发生。
3.准备
- 该阶段正式为类中定义的变量(静态变量)分配内存和设置类变量(静态变量)初始值的阶段。
- 这些变量(静态变量)所使用的内存都应该在方法区中进行分配。但是方法区本身是一个逻辑上的区域,JDK8及以后,类变量则会随着Class对象一起存在java堆中。
- 注意:这个阶段进行内存分配的只有类变量,而不包括实例变量。实例变量将会在对象实例化的时候,随着对象一起分配在java堆中。
- 类变量和实例变量的区别:
- 类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象;
4.解析
- 解析阶段就是虚拟机将常量池中的符号引用替换为直接引用的过程。
- 符号引用:以一组符号来描述所引用的目标
- 直接引用:可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。
- 主要解析的有
- 类或接口解析
- 字段解析
- 方法解析
- 接口方法解析
5.初始化
- 初始化阶段就是执行类构造器()方法过程,()是javac编译的自动生成物。
- ()方法是由编译器自定收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的
- 静态语句块只能访问再静态语句块之前的变量,定义再它之间后的变量。在前面的静态语句块可以赋值,但是不能访问。