java代码编译后,会生成JVM能够识别的二进制字节流文件(*.class)。在Class文件中描述的各种信息,最终需要JVM把其加载到JVM内存中才能运行和使用。
一、概论
JVM把描述类数据的字节码.Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。
类加载都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一些性能开销,但是会给java应用程序提供高度的灵活性。例如:
1.编写一个面向接口的应用程序,可能等到运行时再指定其实现的子类;
2.用户可以自定义一个类加载器,让程序在运行时从网络或其他地方加载一个二进制流作为程序代码的一部分;
(摘自《深入理解Java虚拟机》)
二、如何加载Class文件?
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)七个阶段。
其中类加载的全过程,就是加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)这5个阶段所执行的具体动作。
1、加载(“类加载”中的一个阶段)
此阶段,JVM主要完成下面3件事
1.通过“类的全限定名”来获取定义此类的二进制字节流
2.将字节流所代表的静态存储结构转换为方法区的运行时数据结构
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口
加载阶段是所有阶段中可控性最强的,既可以使用系统提供的类加载器完成,用户也可以自定义类加载器去完成。
注:这个Class对象并没有规定是在Java堆内存中,它比较特殊,对于HotSpot虚拟机而言,它虽为对象,但存放在方法区中。这个对象将作为程序访问方法区中这些类型数据的外部接口。
2.验证
这一步主要的目的是确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。
如果验证的字节流不符合Class文件格式的约束,JVM将抛出java.lang.VerifyError异常或其子类异常.
验证阶段大致包括四个检验动作:文件格式验证、元数据验证、字节码验证和符号引用验证。
2.1文件格式验证:
验证class文件格式规范,例如: class文件是否以0xCAFEBABE开头 , 主、次版本号是否在当前虚拟机处理范围之内等。
该验证阶段目的是保证输入的字节流能正确地解析并存储于方法区之内。
2.2.元数据验证
这个阶段是对字节码描述的信息进行语义分析,以保证起描述的信息符合java语言规范要求。
验证点可能包括:
这个类是否有父类(除了java.lang.Object之外,所有的类都应当有父类)、
这个类是否继承了不允许被继承的类(被final修饰的)、
如果这个类的父类不是抽象类,是否实现了其父类或接口中要求实现的所有方法。
2.3字节码验证
例如 保证跳转命令不会跳转到方法体以外的字节码命令上、
可以把一个子类对象赋值给父类数据类型,这是安全的,但不能把一个父类对象赋值给子类数据类型
2.4.符号引用验证
符号引用中通过字符串描述的全限定名是否能找到对应的类、
符号引用类中的类,字段和方法的访问性(private、protected、public、default)是否可被当前类访问。
3.准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。
注:这时候进行内存分配的仅包括类变量(static 修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在java堆中。
其次这里的初始值是默认初始值
4.解析
解析阶段是虚拟机常量池内的符号引用替换为直接引用的过程。
符号引用:符号引用是一组符号来描述所引用的目标对象,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标对象并不一定已经加载到内存中。
直接引用:直接引用可以是直接指向目标对象的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机内存布局实现相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同,如果有了直接引用,那引用的目标必定已经在内存中存在。
解析的动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。
5.初始化
类的初始化阶段是类加载过程的最后一步。这时才真正开始执行类中定义的java程序代码。在准备阶段,类变量已赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员通过程序制定的主观计划去初始化类变量和其他资源。