代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步。
概要:
虚拟机如果要使用某个JAVA类型,就需要把描述类数据的Class文件加载到内存中,而后对数据进行校验、转换解析和初始化。
与c++相比,Java的类型加载、连接和初始化都是在程序运行期间完成的,这会增加性能开销,同时为Java提供了诸如动态加载和动态连接的特点。例如Applet、JSP、OSGi等。
内的加载时机
一个类型从被加载到内存开始,到卸载出内存为止,它的整个生命周期将会经历加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析三个部分统称为连接。
加载、 验证、 准备、 初始化和卸载这五个阶段的顺序是确定的, 类型的加载过程必须按照这种顺序按部就班地开始, 而解析阶段则不一定。
需要开始类加载过程的第一个阶段“加载”
1)遇到new、getstatic、putstatic或者invokeestatic四条字节码时。
1.1 new实例化对象时。
1.2 读取或设置一个类型的静态字段。
1.3 调用一个类型的静态方法的时候。
2)对类型进行反射调用的时候。
3)初始化类的时候,父类还没有进行过初始化,则需要先触发父类的初始化。
4)指定一个要执行的主类。
5)动态语言支持。
加载
Java虚拟机需要完成以下三件事:
1)通过类的全限定名来获取定义此类的二进制字节流;
从ZIP压缩包中读取—JAR\EAR\WAR
从网络中获取—Web Applet
由运行时计算生成—动态代理技术
由其他文件生成—JSP应用
从数据库中读取—中间件服务器
2)将字节流所代表的静态存储结构转化为方法区的运行时数据结构;
3)生成一个代表这个类的java.lang.Class对象, 作为方法区这个类的各种数据的访问入口。
验证
验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身安全。
-
文件格式验证
验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理:
–是否以魔数0xCAFEBABE开头
–主次版本号是否在当前Java虚拟机接受范围之内
–常量池的常量中是否有不被支持的常量类型(检查常量tag标志)
–指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量; -
元数据验证
对字节码描述的信息进行语义分析,保证符合《java语言规范》:
–当前类是否有父类(除了java.lang.Object之外,所有的类都应当有父类);
–当前类是否继承了不允许被继承的类(被final修饰的类);
–如果这个类不是抽象类,是否实现了其父类或者接口之中要求实现的所有方法。 -
字节码验证
通过数据流分析,确定程序语义是合法的、符合逻辑的。在对元数据中的数据类型校验完毕之后,这个阶段要对类的方法体进行校验分析,保证被校验类的方法在运行时不会做作危害虚拟机的行为。
–任意时刻操作数栈的数据类型与指令代码序列都能够配合工作;
–保证任何跳转指令都不会跳转到方法体以外的字节码指令上;
–保证方法体中的类型转换是有效的。 -
符号引用验证
最后一个阶段的校验行为发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在连接的第三阶段—解析阶段中发生。
–符号引用中通过字符串描述的全限定名是否能找到对应的类;
–在指定类中是否存在符合方法的字段描述符及简单名称所描述的方法和字段;
–符号引用中的类、字段、方法的可访问性(private\protected\public<package>)是否可被当前类访问。
深入理解Java虚拟机:JVM高级特性与最佳实践(第三版)—周志华