前言
今天我们来讲讲jvm里类加载的过程,我们写了那么多类,却不知道类的加载过程,岂不是很尴尬。
jvm的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由jvm的具体实现指定的。[来自官方规范]
jvm组成结构之一就是类装载器子系统,我们今天就来仔细讲讲这个组件。
Java代码执行流程图
大家通过这个流程图,了解一下我们写好的Java代码是如何执行的,其中要经历类加载器这个流程,我们就来仔细讲讲这里面的知识点。
类加载子系统
类加载系统架构图
暂时看不懂这两张图没关系,跟着老哥往下看
类的生命周期
类的生命周期包括:加载、链接、初始化、使用和卸载,其中加载、链接、初始化,属于类加载的过程,我们下面仔细讲解。使用是指我们new对象进行使用,卸载指对象被垃圾回收掉了。
类加载的过程
第一步:Loading加载
通过类的全限定名(包名 + 类名),获取到该类的.class文件的二进制字节流
将二进制字节流所代表的静态存储结构,转化为方法区运行时的数据结构
在内存中生成一个代表该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
总结:加载二进制数据到内存 —> 映射成jvm能识别的结构 —> 在内存中生成class文件。
第二步:Linking链接
链接是指将上面创建好的class类合并至Java虚拟机中,使之能够执行的过程,可分为验证、准备、解析三个阶段。
① 验证(Verify)
确保class文件中的字节流包含的信息,符合当前虚拟机的要求,保证这个被加载的class类的正确性,不会危害到虚拟机的安全。
② 准备(Prepare)
为类中的静态字段分配内存,并设置默认的初始值,比如int类型初始值是0。被final修饰的static字段不会设置,因为final在编译的时候就分配了
③ 解析(Resolve)
解析阶段的目的,是将常量池内的符号引用转换为直接引用的过程(将常量池内的符号引用解析成为实际引用)。如果符号引用指向一个未被加载的类,或者未被加载类的字段或方法,那么解析将触发这个类的加载(但未必触发这个类的链接以及初始化。)
事实上,解析器操作往往会伴随着 JVM 在执行完初始化之后再执行。 符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《Java 虚拟机规范》的Class文件格式中。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
解析动作主要针对类、接口、字段、类方法、接口方法、方法类型等。对应常量池中的 CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。
第三步:initialization初始化
初始化就是执行类的构造器方法init()的过程。
这个方法不需要定义,是javac编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并来的。
若该类具有父类,jvm会保证父类的init先执行,然后在执行子类的init。
类加载器的分类
第一个:启动类/引导类:Bootstrap ClassLoader
这个类加载器使用C/C++语言实现的,嵌套在JVM内部,java程序无法直接操作这个类。
它用来加载Java核心类库,如: