一、类装载流程
-
加载
- 类装载的第一个阶段;
- 获取类的二进制流;
- 将二进制流代表的存储结构转为方法区的运行时数据结构;
- 在java堆中生成对应的java.lang.Class对象。
-
链接 - 验证(确保二进制流中的信息符合java虚拟机规范)
- 文件格式验证
- 是否以0xCAFEBABE开头;
- 版本号是否在虚拟机可接受的范围;
- 常量池中是否有不被支持的常量类型;
- 是否有只想不支持的 或 不存在的常量的索引;
- 元数据验证
- 是否有父类,除Object外都应该有父类;
- 是否继承了不能被继承的类(final修饰的类);
- 非抽象类是否实现了所有父类的抽象方法;
- 子类是否覆盖了父类的final属性;
- 子类是否出现了不符合规则的重载;
- 字节码验证(过程复杂)
- 运行时检查,确定语义是否合法;
- 对方法体进行校验,保证运行时不会损害虚拟机;
- 保证操作数栈的数据类型和指令代码正常配合;
- 保证指令代码能跳转到正确的位置;
- 符号引用验证
- 检查符号引用描述的类是否存在;
- 检查当前类是否有权限访问符号引用中的类、字段、方法。
- 文件格式验证
-
链接 - 准备
- 为类中定义的静态变量(ststic修饰)分配内存并设置初始值;
- public static int a=1 在准备阶段会为a分配内存,并将初始值设为0,在初始化是再将a设为1;
- final static修饰的变量会直接设置为正确值;
- 实例变量不会再准备阶段分配内存,只有再对象实例化时才会随对象一起分配再堆中。
-
链接 - 解析
- 将符号引用替换成直接引用。
-
初始化
- 初始化类构造器;
- static变量赋值,static{}执行;
- 子类初始化前,会先初始化父类;
- 初始化是线程安全的。
-
类装载器 - ClassLoader
- ClassLoader是一个抽象类;
- ClassLoader读入java字节码,将类装载到jvm中;
- ClassLoader负责类装载过程中的加载阶段。
- public Class<?> loadClass(String name) :载入并返回一个class;
- protected final Class<?> defineClass(byte[] b, int off, int len):定义一个类;
- protected Class<?> findClass(String name):查找class;
- protected final Class<?> findLoadedClass(String name):查找已经加载的类。
-
内置ClassLoader
- BootStrap ClassLoader:最顶层ClassLoader,C++实现,加载%JAVA_HOME%/lib/rt.jar,或-Xbootclasspath指定目录下的jar;
- Extension ClassLoader:扩展ClassLoader,继承ClassLoader,加载%JRE_HOME%/lib/ext 目录下的jar包和类,或-Djava.ext.dirs指定目录的jar包;
- App ClassLoader:应用程序ClassLoader,继承ClassLoader,负责加载classpath目录下的jar,或-Djava.class.path指定目录的jar包;
- Custom ClassLoader:自定义ClassLoader,继承ClassLoader自定义的类加载器,加载自定义目录下的jar;
- 双亲委派:加载器接受加载请求之后,会先检查该类是否已被自己加载,若没有加载会委派给父类装载器检查,依次检查完所有父类装载器,当存在父类装载器加载过请求类,则认为该类已被加载;若所有父类装载器都没有加载过该类,则从最顶层父类加载器开始加载该类;(CustomClassLoader —> BootstrapClassLoader);
写两个HelloLoader类,一个打成jar包放在自定义目录下,另一个正常编译,配置jvm参数:-Xbootclasspath/a:C:\study\jvm\classLoader\jvm-project-1.0-SNAPSHOT.jar;
public class HelloLoader { public void print(){ System.out.println("I am in bootloader"); } }
public class HelloLoader { public void print(){ System.out.println("I am in apploader"); } }
执行上述程序,控制台打印并非编译的apploader,证明先加载了-Xbootclasspath/a:C:\study\jvm\classLoader\jvm-project-1.0-SNAPSHOT.jar中的HelloLoader。public class JVMTest extends ClassLoader{ public static void main(String[] args) { HelloLoader helloLoader = new HelloLoader(); helloLoader.print(); } }
- 双亲委派作用:防止类被重复加载,防止核心类被替换;
- 双亲委派存在问题:顶层ClassLoader无法使用下层ClassLoader加载的类;
- Thread. setContextClassLoader():上下文加载器,在顶层ClassLoader中传入底层ClassLoader实例,用以解决顶层ClassLoader访问不到底层Class Loader的问题;