一文带你入门JVM类加载机制
引入
Java源码.java文件
存储程序逻辑,通过编译生成对应的.class字节码文件
。.class文件
中记录着Java代码转换后的虚拟机指令,每个.class文件
开头都有特定标识等信息。
JVM需要每个类时,JVM加载其.class文件
,加载相应字节码信息后,会创建对应的Class对象
,这个过程叫做:Java虚拟机类加载机制。
总结来说:Java源码.java文件
通过编译生成.class字节码文件
,JVM将字节码文件读取进入内存,进行解析、运行等过程得出结果,这个过程就是类加载机制。
1. 类加载过程
类加载过程可以概括为三个步骤:加载(loading)—>链接(linking)—>初始化(initializing)
类加载过程可以分为7个阶段:
加载-->链接(验证-->准备-->解析)-->初始化-->使用-->卸载
-
加载:通过完全限定名查找Class文件二进制数据并将其加载到内存中
-
验证:确保被加载Class的正确性,对Class字节流数据进行校验,验证其是否符合JVM规范
-
准备:JVM为类变量(static)分配内存并初始化为默认值
-
解析:JVM针对类或接口、字段等7类引用进行解析,主要是将类中对常量池内的符号引用转换为直接引用
-
初始化: JVM根据语句执行顺序对类对象进行初始化
-
使用:JVM开始从入口方法开始执行用户的程序代码
-
卸载:JVM开始销毁创建的Class对象,负责运行的JVM也退出内存
2. 类加载器
JVM在加载类的过程中需要使用类加载器进行加载,Java支持四种类加载器,分别是:
- bootstrap classloader(启动类加载器):加载java核心类库;
- extension classloader(扩展类加载器):加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件;
- application classloader(应用程序类加载器):加载当前应用的classpath下的所有类
- user classloader(用户自定义类加载器):用户自定义的类加载器,可加载指定路径的class文件
四种类加载器存在着层级关系,如下图:

类加载器之间并不存在相互继承或包含关系,从上至下仅存在父加载器的层级引用关系。
3 双亲委派机制
3.1 什么是双亲委派机制
当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。
双亲委派的核心思想:自下向上检查类是否已经被加载;从上至下尝试加载类
(图源竹子爱熊猫)
【总结】
Java中提供了四种类加载器,四种类加载器存在层级关系,bootstrap--> extension--> Application --> User,双亲委派机制实际上就是一种选择机制,也就是说类加载的时候需要用哪个类加载器加载。双亲委派机制的逻辑是,当一个类加载器收到加载请求时,不会直接加载,而是把这个请求委托给自己的父加载器(上一级)来处理,如果父加载器都不处理,那就由自己去处理,如果自己也处理不了,那就抛出异常ClassNotFoundException
。
3.2 为什么需要双亲委派机制
使用双亲委派机制能够避免两个问题:
- 问题一:避免同一个类在不同层级的类加载器中重复加载,如果父类加载器已经加载过一次,子类加载器就不用加载了;
- 问题二:保障核心类的安全,有效防止Java的核心API类在运行时被篡改,从而保证所有子类共享同一基础类,减少性能开销和安全隐患问题。例如:输过来一个java.lang.String类,需要被加载时会传到bootstrap类加载器,发现已经被加载,就不会再加载,避免了核心类被篡改的风险。
3.3 破坏双亲委派机制
- 自定义一个类加载器,重写loadClass方法;
- Tomcat可以加载自己目录下的class文件,并不会传递给父类的加载;
- Java的SPI(Service Provider Interface,如:JDBC),发起者BootstrapClassLoader已经是最上层了,直接获取了AppClassLoader进行驱动加载,违反了双亲委派机制。
3.4 双亲委派机制的实现
在Java中,所有的类加载器都间接的继承自ClassLoader类,包括Ext、App类加载器(Bootstrap除外,因为它是C++实现的)
参考资料
转载自:https://juejin.cn/post/7352146423277092904