点赞关注,不再迷路,你的支持对我意义重大!
🔥 Hi,我是丑丑。本文 「Java 路线」| 导读 —— 他山之石,可以攻玉 已收录,这里有 Android 进阶成长路线笔记 & 博客,欢迎跟着彭丑丑一起成长。(联系方式在 GitHub)
前言
- 在 Java 中,编译产物 .class 是一个静态的存储结构,需要加载到内存中的运行时数据结构,才能为我们所用。Java 的类加载机制是很多技术的基础,同时也是面试的重点知识,务必深刻理解。
- 在这篇文章里,我将分析 Java 类加载机制的原理,整个过程分为三个部分:类查找的委派模型 & 类加载的时机 & 类加载的流程。如果能帮上忙,请务必点赞加关注,这真的对我非常重要。
目录
1. 类加载的委派模型
1.1 类加载器 = 命名空间
类的唯一性由两个因素决定:「类的全限定类名」+「类加载器」。类加载器相当于类的命名空间,要判断两个类是否相同,除了判断类名是否相同外,还需要判断是否由同一个 ClassLoader 加载。
提示: 类的唯一性判断会影响:Class#equals、Class#isAssignableFrom()、Class#isInstance()、instanceof 的判断结果。
1.2 类加载的工作机制
Java 类加载是一种委托机制(parent delegate),即:除了顶级启动类加载器(bootstrap classloader)之外,每个类加载器都有一个关联的上级类加载器(parent 字段)。当一个类加载器准备执行类加载时,它首先会委托给上级加载器去加载,而上级加载器可能还会继续向上委托,递归这个过程。如果上级构造器无法加载,才会返回由自己加载。
ClassLoader.java
public abstract class ClassLoader {
上级类加载器
private final ClassLoader parent;
protected Class<?> loadClass(String name, boolean resolve) {
1、检查是否加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
2.1 递归委派给上级类加载器
c = parent.loadClass(name, false);
} else {
2.2 委派给顶级启动类加载器
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
2.3 上级类加载器无法加载,抛出异常
}
3. 交给自己加载,findClass() 我后文再说
if (c == null) {
c = findClass(name);
}
}
return c;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
}
提示: 只有 bootstrap classloader 的 parent 字段为空。
1.3 “双亲” 委派是不好的翻译
在很多资料里会提到 “双亲委派机制” 的概念,这其实是不信达雅的翻译了。 “双亲” 原文是 parent delegate,在 Java 文档原文的意思:将加载类的任务委派给上一级类加载器,而双亲的翻译却让人摸不着头脑。
The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a "parent" class loader. When loading a class, a class loader first "delegates" the search for the class to its parent class loader before attempting to find the class itself.
1.4 优点
- 1、复用: 使用上级类加载器加载过的类,避免重复加载;
- 2、安全(基础类型的一致性): 防止加载被篡改的基础类 .class文件。
1.5 破坏委派模型
- 1、兼容jdk1.2,loadclass不禁止重写
- 2、Bootstrap ClassLoader加载用户类(解决方案:Thread Context ClassLoader)
2. Java 中的类加载器
笔者是 Android 党,这部分内容不熟悉,有缘再见。如果你对 Android 中的类加载器感兴趣,可以看下同系列的这篇文章:「Android 路线」插件化的基本原理
3. 自定义的类加载器
Editting...
4. 类加载的时机
Editting...
5. 类加载的流程
5.1 概述
在类加载阶段,虚拟机主要完成三件事情:
- 1、通过「类的全限定类名」来加载 「.class 文件的二进制字节流」;
- 2、将「.class 字节流」转换为「类的运行时数据结构」(存储在方法区);
- 3、在 Java 堆中生成一个「类的 Class 对象」,它将作为方法区类数据的访问入口。
其中第 2 步,将 .class 字节流转换为类的运行时数据结构的步骤,具体分为:
- 1.加载:获取类的二进制字节流;生成方法区的运行时存储结构;在内存中生成 Class 对象 2.验证:确保该 Class 字节流符合虚拟机要求 3.准备:初始化静态变量 4.解析:将常量池的符号引用替换为直接引用 5.初始化:执行静态块代码、类变量赋值
Editting...
6. so 库加载流程
除了加载类,ClassLoader 还需要负责加载 so 库,这个问题在我之前写过的一篇文章里分析过了:「NDK 路线」| so 库加载到卸载的全过程。
7. 总结
Editting...
参考资料
- 《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》(第 7 章) —— 周志明 著
- 《深入理解Android:Java虚拟机ART》(第 8 章)—— 邓凡平 著
- 《你知道 Java 类是如何被加载的吗?》 —— 汪先登 著(阿里巴巴)
创作不易,你的「三连」是丑丑最大的动力,我们下次见!