JVM加载class流程
Source
-> .class
-> ClassLoader
-> RuntimeData
(运行时区域, JVM的内存结构模型)
加载 class 具体流程
程序启动起来就是运行时, 任何程序都有运行时. 所有都编译成机器码的问题在于跨平台. 编译成不同平台机器码的过程叫做交叉编译.
GC 除了是内存回收工具, 还是 内存分配工具, 也就是说是 JVM Heap
的管控者.
本地库接口是任何操作语言都必须有的. 因为操作系统掌控着所有资源, 一定要和操作系统打交道. 除非找到安全漏洞, 绕过操作系统.
ClassLoader
将 .class
文件导入 JVM
, 形成运行时的类. 类的装载流程:
- 加载 : 通过
ClassLoader
加载class
文件字节码, 生成Class 对象
. - 链接: 就是将已经读入内存的类的二进制合并到 JVM 运行时环境中.
loadClass()
方法中的resolve
参数就是判定是否要解析该类。- 验证 : 检查加载的
class
的正确性(符合规范)和安全性. - 准备 : 为类变量分配存储空间并设置类变量初始值(默认值).
- 解析 : JVM 将常量池内的符号引用转换为直接引用(JVM或其它运行时环境所能直接使用的形式).
- 验证 : 检查加载的
- 初始化 : 执行类变量赋值和静态代码块, 为类的静态变量赋值.
- 使用
- 卸载
注意: loadClass
和 forName
的区别
Class.forName
得到的 Class 是已经初始化完成了的。ClassLoader.loadClass
的得到的Class
是还没有解析的。
像 mysql-connector 就需要使用 forName 的方式加载, 因为是在 static 代码块里面进行初始化的.
Spring 的 IOC 就使用到 loadClass, 延迟加载, 不需要初始化和链接的步骤, 这样可以加快加载速度, 把类的初始化放在需要用到的时候才初始化.
执行引擎执行 byteCode
和 处理 GC
执行引擎调用本地库接口, 执行本地方法. 本地方法执行时使用 Native Stack
(可能形成 Native Memory)
JIT 运行时优化. 处理器不同, 执行的指令会有所区别, 所以可以使用该处理器的一些高效指令.
类的加载方式
- 隐式加载 : new
- 显式加载 : loadClass, forName
双亲委派模型
class 默认的加载方式. ClassLoader
主要用于装载阶段, 其主要作用从系统外部获得 Class
二进制数据流. 它是 Java
的核心组件, 所有的 Class
都由 ClassLoader
进行加载, ClassLoader
负责通过将 Class
文件的二进制数据流装载进系统, 然后交给 JVM
进行链接(校验, 准备, 解析), 初始化, 使用, 卸载等操作.
ClassLoader 接收的是 .class
, 产出 byteCode
.
只有一个 ClassLoader
需求多样
- 从文件
- 从内存
- 从
TCP/UDP/HTTP
- 从其它
Library
- 其它策略
- 缓存策略
- 安全策略
- 统计策略
- 代理策略
考虑封装, 继承, 组合, 考虑各种复用手段.
版本问题
树状结构, 将Module1
, Module2
分开了.
安全策略
Foo
和 Bar
之间是不可见的.
- 委托父类加载
- 父子之间信任关系
MetaPrograming
: 运行时生成 Class
, 然后执行. 像 AspectJ
这种 AOP
框架, 就很需要.
需要什么模型?
树状关系
- Root Class Loader
- Leaf Class Loader
委托关系
- 子
Class Loader
委托父Class Loader
完成工作 - 缓存设置到父节点
ClassLoader 四层模型
BootStrap Class Loader: 加载核心类, 例如: Standard JDK. 使用 C++
编写的.
Extensions Class Loader: 加载扩展类. 例如JRE目录下面的 lib/ext
(JDK8以及之前). JDK 9 之后是 PlatformClassLoader. 因为整个 JDK 基于模块化进行构建了.
Application Class Loader: classpath
Custom Class Loader: 用户自定义
加载流程
先判断自己有没有加载, 如果没有向上传递, 委托给上一层加载. 如果上一层尝试加载失败, 给下一层尝试加载.
双亲: 指父亲和母亲…但是 ClassLoader
就一个父亲…
委派倒是可以理解, 向上一层委托. 避免多份字节码的加载.
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 {
// 调用父级的 classLoader
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();
// 这个方法是没有实现的, 直接抛异常, 需要子类去实现重写
c = findClass(name);
// this is the defining class loader; record the stats
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
// resolve 是否要链接(解析)这个类
if (resolve) {
resolveClass(c);
}
return c;
}
}
所以, 自定义类加载器, 要实现ClassLoader
类的findClass
方法, 然后调用defineClass
转成class
对象.
远程加载class
.
如果要深入探讨, 探索下 OSGi(Open Service Gateway Initiative, 开放服务网关协议) 模型, 是 Java动态化模块系统的一系列规范.