类加载子系统
结构简图
一、类加载器的三个阶段
分为
- 加载阶段
- 链接阶段
- 初始化阶段
主要工作:
- 从文件当中或者网络当中加载class文件,class文件打开有特殊的文字标识。
- classLoader只是负责文件的加载,至于是否可以执行成功,就是看ExecutionEngine(执行引擎)决定。
- 加载的文件保存在方法区,以及一些常量放置在常量池(字符串常量池、byte常量池等)
ClassLoader
class file -> JVM -> 元数据模板
类加载过程
链接阶段
- 验证阶段
保证加载文件的正确性 文件格式 元数据验证 字节码验证 符号引用验证
比如 字节码文件 验证 CA FE BA BE (咖啡北鼻)用来验证
2. 准备阶段
只会赋予初始值,如果是final修饰,就会在编译阶段进行赋值(类变量,就是静态变量,会在这里初始化,但是不会被赋值) - 解析阶段
本动作是在初始化之后执行
初始化阶段
clinit方法就是初始化静态变量以及静态代码快当中的变量,是jvm自己调用,不需要手动操作,如果没有类变量和静态代码快,就没有clinit方法
举例:
package com.ll.tree;
public class TestMy {
static int i = 10;
static{
i = 100;
num = 100;//这里可以访问num ,是因为下链接当中的准备阶段就已经初始化完成了,此时由上而下执行会导致num先赋值100,在赋值10,可以赋值,不能调用
}
static int num = 10;
public static void main(String[] args) {
}
}
// access flags 0x8
static <clinit>()V
L0
LINENUMBER 8 L0
BIPUSH 10
PUTSTATIC com/ll/tree/TestMy.i : I // i先赋值10 再赋值100
L1
LINENUMBER 10 L1
BIPUSH 100
PUTSTATIC com/ll/tree/TestMy.i : I
L2
LINENUMBER 11 L2
BIPUSH 100
PUTSTATIC com/ll/tree/TestMy.num : I // i先赋值100 再赋值10
L3
LINENUMBER 13 L3
BIPUSH 10
PUTSTATIC com/ll/tree/TestMy.num : I
RETURN
MAXSTACK = 1
MAXLOCALS = 0
}
如果没有静态变量和代码快就没有clinit方法
public class JVMtest1 {
int i = 1;
public static void main(String[] args) {
System.out.println(1);
}
}
没有clinit方法
- init 方法就是构造器
- clinit方法 是否被同步加锁 如果
比如说同一个类,在static代码块当中设置一个死循环,使得clinit方法无法执行结束,其他线程就无法创建该对象
package com.company.test.jvm;
/**
* @author Mr.Lian
* @create 2021-09-04 14:28
**/
public class JVMtest1 {
public static void main(String[] args) {
ClinitThread c = new ClinitThread();
ClinitThread c1 = new ClinitThread();
c.start();
c1.start();
}
}
class ClinitStudy{
static int i = 0;
static {
while (i <1) {}
}
}
class ClinitThread extends Thread{
@Override
public void run() {
System.out.println(this.currentThread().getName()+"进来了");
ClinitStudy clinitStudy = new ClinitStudy();//创建对象 在执行clinit方法 停在这
System.out.println(this.currentThread().getName()+"创建完成");
}
}
类加载器分类
分类
第一个就是引导类加载器,下面是自定义引导类,凡是继承ClassLoader 就是自定义类加载器
扩展类加载器
系统类加载器
获取类加载器
获取上一层加载器
ClassLoader parent = systemClassLoader.getParent();
为什么要自定义加载类?
加载自定义路径下的class文件
- 我们需要的类不一定存放在已经设置好的classPath下(有系统类加载器AppClassLoader加载的路径),对于自定义路径中的class类文件的加载,我们需要自己的ClassLoader
进行类的加密和解密操作
- 有时我们不一定是从类文件中读取类,可能是从网络的输入流中读取类,这就需要做一些加密和解密操作,这就需要自己实现加载类的逻辑,当然其他的特殊处理也同样适用。
实现热部署等
- 可以定义类的实现机制,实现类的热部署,如OSGi中的bundle模块就是通过实现自己的ClassLoader实现的。