Java中类的加载器及其加载过程
作者:何小幸
Java中类的加载器及其加载过程
概述
字节码文件在类加载器子系统中要进行加载、链接、初始化等处理,我们这里详细来谈其中过程
类加载器子系统负责从文件系统或网络中加载Class文件,且ClassLoader只负责class文件的加载,至于其能否运行,则交由ExecutionEngine来判断。
加载的类信息存放于一块称为方法区的内存空间。除了类的信息以外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)
👆class文件最终加载到JVM中,根据这个文件可以实例化出n个一模一样的实例,加载到JVM的class文件则被称为DNA元数据模板,放在方法区。而在这整个文件到元数据模板的过程中,类装载器就起到了一个转换器的作用
转换和执行过程:
首先需要装载目标类,再进行链接,然后对其进行初始化,最终调用对应类中的方法
所以,类的加载过程如下👇
过程一:Loading
这个过程也称为加载,但和类的加载不同,这个加载只是其中的一个环节,切莫搞混,我们这里用Loading来代替,以防混淆
Loading过程:
- 通过一个类的全限定名获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法去的运行时数据结构
- 再内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
加载.class文件的几种方式:
- 从本地系统中直接加载
- 通过系统网络获取,典型场景:Web Applet
- 从zip压缩包中获取,成为日后jar、war格式的基础
- 运行时计算生成,使用最多的是:动态代理技术
- 由其他文件生成,典型场景:JSP应用
- 从专有数据库中提取.class文件,比较少见
- 从加密文件中获取
过程二:Linking
链接阶段包括验证(Verify)、准备(Prepare)、解析(Resolve)
Verify
目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会威胁虚拟机自身的安全。
验证有四种形式:
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
e.g.:
class字节码文件中以CAFEBABE开头,以供识别
Prepare
该阶段为类的变量分配内存表并且设置该变量的默认初始值,即零值。
不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会进行显式初始化
同样也不会为实例变量分配初始化,类变量会分配在方法区中,但实例变量会随着对象一起分配到java堆中
Resolve
该阶段将常量池内的符号引用转换为直接引用的过程
事实上,解析操作往往会伴随着JVM在执行完初始化之后再执行
符号引用就是一组符号来描述所引用的目标,符号引用的字面量形式明确定义在《java虚拟机规范》的class文件格式中。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等
过程三:Initialization
该过程是执行类构造器方法()的过程
此方法不需定义,是javac虚拟机自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来,所以如果没有这两项操作,就不存在这个过程。
这里会出现一个神奇的现象:
static{ number = 20; } private static int number = 10;
这样也可以执行,而执行结果为10,究其原因,number在prepare中早已初始化,并赋值为0,而来到Initialization阶段中才会开始进行赋值,首先执行static块中的number = 20,后再执行number = 10的赋值操作,最后结果为10。
*但在static中不能对其进行引用/调用,比如打印操作,会报错:Illegal forward reference*
构造器方法中指令按语句在源文件中出现的顺序执行
()不同于类的构造器(关联:构造器是虚拟机视角下的())
若该类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕
虚拟机必须保证一个类的()方法在多线程下被同步加锁
到此这篇关于Java中类的加载器及其加载过程的文章就介绍到这了,更多相关Java类加载器及过程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!