类加载机制
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
类加载过程
主要介绍几个重要阶段:加载、验证、准备、解析和初始化
加载
在加载阶段,虚拟机主要执行以下三个操作
1)、通过类的全限定名来获取定义这个类的二进制字节流。
2)、将这个字节流所代表的静态存储结构转化成方法区的运行时数据结构。
3)、在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口。
这个阶段相比其他阶段来说,是开发人员可控性最强的阶段。因为这个阶段既能使用系统提供的加载器(这个加载器后面会进行介绍)加载,又能通过开发人员自定义的加载器进行加载。
在加载这个阶段还有一个需要注意的地方,在执行第一个操作时,需要知道可以从哪里获取class文件,例如:
1)、从压缩文件中读取(JAR,WAR等)
2)、从本地磁盘中获取
3)、从网络上获取(Applet)
4)、运行过程中动态生成(动态代理)
5)、其他文件生成(jsp生成对应的class文件)
6)、从数据库中读取
验证
验证阶段主要有4个阶段的验证:文件格式验证、元数据验证、字节码验证和符号验证
1、文件格式验证
这一阶段要验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理,主要包括魔数、版本号、常量池等验证。
2、元数据验证
这个阶段是对字节码描述的信息进行语义分析,以保证其描述的信息符合java语言规范的要求。主要包括是否有父类,类中的字段、方法是否与父类冲突,如果不是抽象类,是否实现了其父类或接口中要求实现的所有方法等;
3、字节码验证
这个阶段是在元数据验证之后,对类的方法体进行校验分析,保证被校验类的方法在运行时不会做出危害虚拟机的安全事件,主要目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。也是验证过程最复杂的一个阶段。
4、符号引用验证
这个阶段的校验发生在虚拟机将符号引用转化为直接引用的时候。是对类自身以外的信息进行匹配性校验。主要目的是确保解析动作能正常执行。
准备
准备阶段是为类变量分配内存并设置类变量初始值的阶段,分配这些内存是在方法区里面进行的,这个阶段有两点需要重点介绍以下的:
1)、只有类变量(被static修饰的变量)会分配内存,不包括实例变量,实例变量是在对象实例化的时候在堆中分配内存的。
2)、设置类变量的初始值是数量类型对应的默认值,而不是代码中设置的默认值。例如public static int number=111,这类变量number在准备阶段之后的初始值是0而不是111。而给number赋值为111是在初始化阶段。
解析
解析阶段是虚拟机将常量池里内的符号引用转换为直接引用。这里注意2个概念:
1)、符号引用:以一组符号来描述所有引用的目标,符号可以是任何形式的字面量,只要使用时能正确定义到目标即可。
2)、直接引用:可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符等7类符号引用进行的。
初始化
这个阶段是类加载过程的最后一步,是代码真正开始执行的时候,在这个阶段,开发人员可以根据自己的需求去给类变量初始化赋值。简单来说就是执行类构造器()方法的过程。