隔了好久终于把这篇文章补上了,最近在看《深入理解Java虚拟机》,一本很不错的书,必须值得一看。
由于本人对Java类的加载过程一直是一知半解,所以优先看了一下这章,在这里,我跟大家分享一下,有理解不到位的地方还希望大家指出。
一、为什么需要
我们都知道不管是什么程序,都是通过我们能够识别的语言转变为机器可以识别的语言,才能够实现我们想要做到的功能。目前,我们把这种转换分为两类,也就是编译型语言和解释型语言。
——————————————————————————————————
编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成 为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。
解释型语言:程序不需要编译,程序在运行时才翻译成机器语言,每执 行一次都要翻译一次。
——————————————————————————————————
总之,编译型语言就是提前翻译好了,直接用就可以,而解释型语言就是边看边翻译。而Java语言是两种形式的结合,它需要先提前编译成.class文件,然后再在运行时将.class文件解释为机器语言。而这个解释的任务就交由jvm来处理,处理的第一步就是.class文件的加载过程。
二、如何实现
类加载机制主要有三大部分,加载、连接、初始化,连接又分为验证、准备、解析。
注:加载、验证、准备、初始化的顺序是固定的,解析不一定在初始化之前也有可能在初始化之后。
1、加载
我们平常说的加载大多不是指的类加载机制,只是类加载机制中的第一步加载。在这个阶段,JVM主要完成三件事:
定位:通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件)。而获取的方式,可以通过jar包、war包、网络中获取、JSP文件生成等方式。
转换:将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。这里只是转化了数据结构,并未合并数据。(方法区就是用来存放已被加载的类信息,常量,静态变量,编译后的代码的运行时内存区域)
入口:在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。这个Class对象并没有规定是在Java堆内存中,它比较特殊,虽为对象,但存放在方法区中。
2、连接
- 验证
文件格式:第一阶段要验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。
元数据:第二阶段是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范的要求。
字节码:第三阶段是整个验证过程中最复杂的一个阶段,主要目的是通过数据流和控制流分析,确定程序语法是否是合法的,符合逻辑的。在第二阶段对元数据信息中的数据类型做完校验后,这个阶段将对类的方法体进行校验分析,保证被校验类的方法在运行时候不会做出对虚拟机有危害的事情。
符号引用:最后一个阶段的校验发生在虚拟机将符号引用转化为直接引用的时候,这个转换动作发生在连接的第三阶段–解析中发生,符号引用可以看做是对类自身以外的信息进行匹配性校验。
- 准备
准备阶段是正式为类变量 分配内存并设置变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配,记住,只为类变量分配内存,不包括实例变量,实例变量将会在对象实例化时随对象一起分配在java堆中。
例如static int a=3,在此阶段会a为类变量被初始化为0,其他数据类型参考成员变量声明。只有在初始化的时候才将a初始化为3。但是如果是final static int a =3,说明a是ConstantValue属性,那么在准备阶段变量a就会被初始化为3。
- 解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,对同一个符号引用进行多次解析请求是很常见的事情,虚拟机不会重新再解析而是通过缓存去拿出解析的数据,但是invokedynamic指令除外,它会每次被解析都会被重新解析,解析动作主要针对类,接口,字段,类方法,接口方法,方法类型,方法句柄和调用点限定符7类符号引用进行,
——————————————————————————————————
符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用 的目标并不一定已经加载到内存中。
直接引用:直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的,如果有了直接引用,那么引用的目标必定已经在内存中存在。
3、初始化
初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证方法执行之前,父类的方法已经执行完毕。p.s: 如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成()方法。
三、整体流程
总结:
jvm中类加载的过程就分为这三步,加载、连接(验准解)、初始化 ,而对于初始化的时机和加载器什么的都没有提到,也就留到下次再说吧。希望大家能够持续关注。