一. 类加载机制三大步骤
1. 加载
- 获得二进制流:获得.class文件的字节流,有多种方式获得.class文件,如:zip包、网络、运行时计算生成等。
- 数据类型转换:将上一步的字节流以一定的数据结构(魔数、主次版本号、访问权限、常量池、字段表、方法表、属性表等)保存至方法区。
- 生成Class对象:在内存中生成一个代表该类的java.lang.Class对象。一般情况下,该类是在堆中的,但对于HotSpot虚拟机,则保存在方法区中。
2. 连接
- 校验
- 四大阶段
- Class文件格式校验(魔数、主次版本号...)
- 元数据校验(是否有父类...)
- 字节码校验(把一个子类对象赋值给父类数据类型,这是安全的...)
- 符号引用校验(通过全限定名是否能找到对应的类...)
- 作用? 确保.class文件的字节流数据符合当前虚拟机的要求,并保证虚拟机的安全。
- 四大阶段
- 准备
- 作用? 为类变量在方法区分配内存,并设置零值。
- 解析
- 作用? 虚拟机将方法区中常量池的符号引用替换为直接引用。
3. 初始化
- 作用? 将用户指定的值赋给类变量,以及执行静态代码块。
- 类变量赋值与静态代码块赋值顺序? 顺序执行,后面的覆盖前面的!
public class ClassLoaderSequence { public static int id = 1; static { age = 10; id = 2; } public static int age = 20; } @Test public void staticExcSequenceTest(){ System.out.println("id = " + ClassLoaderSequence.id); // 2 System.out.println("age = " + ClassLoaderSequence.age); // 20 }
二. 类加载器分类
- 引导类加载器(Bootstrap ClassLoader)
- 扩展类加载器(Extension ClassLoader)
- 系统类加载器(System ClassLoader)
- 用户类加载器(User ClassLoader)
三. 疑问
1. 什么时候触发类加载的加载阶段?
- 运行时,用到某个类时。
- 遇到四大字节码指令。
- new关键字实例化对象。
- 读取(getStatic)、设置(putStatic)类字段。
- 调用类方法(invokeStatic)。
- 对类进行反射调用。
- 调用java.lang.reflect包中方法。
- 父类尚未加载。
- 当初始化某个类时,若发现该父类尚未初始化,则优先触发父类初始化。
- 当初始化某个类时,若发现该父类尚未初始化,则优先触发父类初始化。
- Java虚拟机规范允许系统预加载某些类。
- 哪些类? 系统经常用到的,如:java.util.*、java.lang.*、sun.reflect.*
- 如何证明? 启动项目时添加-XX:+TraceClassLoading虚拟机参数。
四. 参考
本文中,有很多细节没有展开论述,原因是因为有篇很好的文章(https://www.cnblogs.com/xrq730/p/4844915.html)已经讲得很清楚了,我也就没有必要再做赘述。
- 《深入理解Java虚拟机》第七章 - 虚拟机类加载机制
- 《疯狂Java讲义》 第十八章 - 类加载机制与反射(上半部分)