1、类的加载
1. 装载类的第一阶段
2. 获取类的二进制流
2.1 从本地系统中直接加载
2.2 通过网络下载.class文件
2.3 从zip,jar等归档文件中加载.class文件
2.4 从专有数据库中提取.class文件
2.5 将Java源文件动态编译为.class文件
3. 转为方法区的数据结构
4. 在java堆中生成java.long.Class对象
2、链接
- 2.1 验证
1. 文件格式验证
1.1 是否以0xCAFEBABE开头
1.2 版本号是否合理
1.3 常量池中的常量是否有不被支持的类型
2. 元数据验证【简单的说就是java中基本的语法和语义是否符合规范】
2.1 是否有父类
2.2 继承了final类
2.3 非抽象类是否实现了所有的抽象方法
3. 字节码验证【很复杂 我也不是很懂 ╮( ̄▽ ̄)╭ 】
3.1 运行检查
3.2 栈数据类型和操作码数据参数吻合
3.3 跳转指令到合理的位置
4. 符号引用验证
4.1 常量池中描述类是否存在
4.2 访问的方法或者字段是否具有足够的权限【public、protected、private】
- 2.2 准备
1. 分配内存,并为类设置初始值(方法区中)
2. public static int v =1;
3. 在准备阶段中,v会被值为0;
4. 在初始化的<clinit>中才会被值为1;
5. 对于static final类型,在准备阶段就就会被赋上正确的值
6. public static final int v =1;
- 2.3 解析
1. 将符号引用替换直接引用;
2. 什么是符号引用
2.1 符号引用就是字符串 假设我有一个User类默认超类就是java.lang.Object 那么它在class的常量池中有个字符串 字符串就是"java.lang.Object"
2.2 直接引用就是指针和地址偏移量
3. 初始化
1. 执行类构造器<clinit>
1.1 变量 赋值语句
1.2 static{}语句会被执行
2. 子类的<clinit>调用前保证父类的<clinit>被调用
3. <clinit>是线程安全的;
4.ClassLoader
- 4.1 什么是类装载器ClassLoader
1. ClassLoader是一个抽象类
2. ClassLoader的实例将读入Java字节码将类装载到JVM中
3. ClassLoader可以定制,满足不同字节码流获取方式
4. ClassLoader负责类装载过程中的加载阶段
- 4.2 ClassLoader的种类
1. BootStrap ClassLoader (启动ClassLoader)
2. Extension ClassLoader (扩展ClassLoader)
3. App ClassLoader (应用ClassLoader/系统ClassLoader)
4. Custom ClassLoader(自定义ClassLoader)
5.双亲委托机制
-
5.1 双亲委托机制图解
5.2 双亲委托机制源码解析
1. 检查类是否已被加载过,调用 findClassLoaded() 查看当前类加载器是否存在 class 的缓存
2. 若类未被加载过,递归委托父类加载器调用 loadClass() 加载类,若无,则 findBootstrapClassOrNull() 完成类加载
3. 若以上步骤都不能完成类加载,则调用 findClass() 尝试当前类加载器完成加载,若加载成功则缓存
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 检查类是否已被加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//递归委托父类加载器加载类
c = parent.loadClass(name, false);
} else {
//无父类加载器,则调用启动类加载器加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
long t1 = System.nanoTime();
//递归委托后仍然无法完成类加载,则使用当前类加载器加载
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
- 5.3 双亲委托机制意义
1. Class的唯一性【同一个加载器加载同一份class文件】
2. 保证Java程序安全稳定运行