类加载过程
1. 加载
在加载阶段,java虚拟机需要完成以下三件事情:
- 通过一个类的全限定名来获取定义此类的二进制流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个java.lang.class对象,作为方法区这个类的各种数据访问入口。
加载阶段结束后,java虚拟机外部字节流就按照虚拟机规定的格式存储在方法区中了,之后会在java堆内存中实例化一个java.lang.class对象1,这个对象作为程序访问方法区中的类型数据的外部接口。
2.验证
确保class文件的字节流中包含的信息符合《java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。
- 文件格式验证
验证字节流是否符合class文件格式规范,并能被当前版本虚拟机处理。- 元数据验证
对字节码描述的信息进行语义分析,以保证其描述的信息符合《java语言规范》的要求。- 字节码验证
通过数据流分析和控制流分析,确定程序语义是否合法、符合逻辑。- 符合引用验证
对类自身以外的各种类型进行匹配性校验,检查该类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。
3.准备
正式为类中定义的变量(静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。从概念上讲,这些变量所使用的内存都应该放在方法区,但是在JDK及之前都又永久代实现,实现是完全符合逻辑概念的。在JDK8及以后,就随着class对象一起存放在堆内存中,这时候“类变量在方法区”就完全是逻辑概念的表述了。
4.解析
java虚拟机讲常量池中的符号引用替换为直接引用,符合引用在class文件中以constant_class_info等类型的常量出现
符合引用:以一组符合来描述所引用的目标,符合可以是任何形式的字面量,只要能无歧义的定位到目标即可,符合引用与虚拟机
直接引用:可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的语柄。直接引用和虚拟机实现的内存布局相关的,同一个符号在不同虚拟机中的直接引用可能不同。若有了直接引用,那引用的目标必定在虚拟机中的内存存在。
5.初始化
是类加载最后一个步骤,之前的几个阶段除了加载阶段用户应用程序可以通过自定义类加载器的方式外,其余都是由java虚拟机来主导控制。直到初始化阶段,java虚拟机才真正开始执行类中的编写的java代码,将主导权交给应用程序。
准备阶段,变量已经有初始值,在初始化阶段,会根据程序员通过程序编码制定的主观计划去初始化类变量和其他资源。