文章目录
- 前言
- 一、加载
- 二、链接
- 验证
- 准备
- 解析
- 三、初始化
- 发生的时机
- 不会触发类的初始化
- 四、类加载器
- 双亲委派模式
前言
Java 的类加载阶段分为:加载、链接、初始化,而链接的过程中包括:验证、准备、解析。
一、加载
将类的字节码载入方法区中,内部采用 C++ 的 instanceKclass 描述 Java 类
instanceKclass 的重要字段:
- _java_mirror:Java 类镜像,存放类对象的地址,例如对于 String,存放的就是String.class
- _super:即父类
- _methods:即方法
- _constants:即常量池
- _class_loader:类加载器
- _vtable:虚方法表
- _itable:接口方法表
二、链接
链接阶段包括验证、准备、初始化
三个部分
验证
验证类是否符合 JVM 规范,进行安全性检查,例如:检查 Java 文件的魔数
准备
为 static 变量分配地址空间,设置默认值
- static 变量分配空间和赋值是两个步骤,分配空间是在准备阶段完成,而赋值是在初始化阶段完成;
- 若 static 变量是 final 类型的,且变量类型为基本数据类型或者字符串对象,则在准备阶段进行赋值;
- 若 static 变量是 final 类型的,且变量类型为引用对象,则仍在初始化阶段进行赋值;
解析
将常量池中的符号引用解析为直接引用
三、初始化
初始化调用 ()v,即执行类的构造方法,代码块等,虚拟机会保证这个类的线程安全
发生的时机
概括的说,类的初始化是懒惰的
- main 方法所在的类,总会被首先初始化;
- 首次访问类的静态变量或者静态方法,因为这些变量不是 final 的,会在类的初始化阶段进行赋值;
- 子类初始化,会引起父类的初始化;
- 子类方法父类的静态变量,会导致父类的初始化;
- Class.forName;
- new 会导致初始化;
不会触发类的初始化
- 访问类的 static final 静态变量(基本类型和字符串),不会被初始化,因为这些变量的赋值是在准备阶段;
- 类对象.class 不会触发类的初始化;
- 创建该类的数组不会触发初始化;
- 类加载器的 loadClass 方法;
- Class.forName 的参数 2 为 false 时
四、类加载器
以 JDK8 为例
名称 | 加载哪里的类 | 说明 |
---|---|---|
Bootstrap ClassLoader | JAVA_HOME/jre/lib | 无法直接访问 |
Extension ClassLoader | JAVA_HOME/jre/lib/ext | 上级为 Bootstrap ClassLoader,访问为 null |
Application ClassLoader | classpath | 上级为 Extension ClassLoader |
自定义类加载器 | 自定义上级 | 为 Application ClassLoader |
类加载器的作用:加载类的二进制字节码
双亲委派模式
双亲委派模式是一种Java类加载机制,它定义了一种层次化的父子关系,由父类加载器向下委派请求,直到找到合适的类加载器为止。
- 首先会检查缓存,查找该加载器是否已经加载过这个类了,如果没有就去父类的加载器中去寻找;
- 如果缓存中没有找到,就会自顶而下用的类加载器去创建该类;
- 最终返回该类即可;
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded// 检查缓存,是否已经加载过这个类Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {// 向上级的累加器中找c = parent.loadClass(name, false);} else {// 向 Bootstrap 类加载器中找c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}// 如果缓存没有找到,就去创建if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();// 调用 findClass 去创建类c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}