JVM类加载机制
目录
类加载的时机
有且之后一下六中情况才会发生类的初始化:
- 遇到
new
、getstatic
、putstatic
或invokestatic
这四条字节码指令时,如果类型没有进行过初始化,则需要先触发其初始化能触发者条指令的场景:- new关键字
- 读取或者设置一个静态字段
- 调用一个类型的静态方法
- 使用
java.lang.reflect
- 如果父类没有初始化, 需要先完成父类的初始化
- 虚拟机需要先初始化主类(
main
) - 当使用JDK 7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。
- default关键字修饰的接口方法, 如果其实现类发生了初始化, 则这个接口需要在他之前初始化
加载
加载阶段的任务:
- 通过全限定名来获取此类的二进制字节流
- 将读取到的字节流中的所描述的静态存储结构转成方法区的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.class对象, 作为方法区这个类的各种数据的访问入口
验证
文件格式验证
元数据验证
字节码验证
准备
准备阶段是正式给类中定义的变量(静态变量
) 分配内存并设置初始值, 而将该变量赋值为最终值实在初始化阶段
注意: 如果该静态变量如果被final修饰了, 则在该阶段就会赋上最终值, 应为在准备阶段会初始化有的ConstantValue
解析
解析阶段是将Java虚拟机中常量池的引用替换为直接引用的过程
类和接口的解析
字段的解析
方法的解析
接口方法的解析
初始化
是类加载过程的最后一步。
会执行类构造器<clinit>()
方法
<clinit>()
这个方法不是必须的, 如果一个类中没有静态代码块, 也没有变量的赋值操作, 则编译器可以不为这个类生成<clinit>()
方法
双亲委派模型
类加载器种类
-
启动类加载器(Bootstrap Class Loader)
由c++语言实现, 负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等
-
扩展类记载器(Extension Class Loader)
负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的jar包
-
应用程序类加载器(Application Class Loader)
负责加载ClassPath路径下的类包,主要就是加载我们自己写的那些类
-
自定义类加载器(Custom Class Loader)
负责加载用户自定义路径下的类包
为什么要设计双亲委派机制
-
沙箱安全机制
如: 自己写的java.lang.String.class类不会被加载,这样可以防止核心API库被随意篡改
-
避免类的重复加载
当父亲已经加载了该类时,就没必要子ClassLoader再加载一次,保证被加载类的唯一性
Tomcat类加载机制
- CommonClassLoader:Tomcat 最基本的类加载器,加载common文件夹lib,加载路径中class可以被Tomcat和各个webapp访问。
- CatalinaClassLoader:Tomcat 私有类加载器,加载server文件夹lib,webapp不能访问其加载路径下的class,即对webapp不可见。
- SharedClassLoader:各个webapp共享的类加载器,对Tomcat不可见。
- WebappClassLoader:webapp 私有的类加载器,只对当前webapp可见。
- JasperClassLoader:JSP的类加载器,每个web应用程序都对应一个WebappClassLoader,每个 jsp文件对应一个JasperClassLoader,所以这两个类加载器有多个实例。
分别加载对应文件夹下的lib
|
|
Tomcat7之后合并了common、server、shared文件夹
Tomcat 应用的默认加载顺序
- 先从JVM的BootStrapClassLoader中加载。
- 加载Web应用下/WEB- INF/classes中的类。
- 加载Web应用下/WEB-INF/lib/* .jap中的jar包中的类。
- 加载上面定义的System路径下面的类。
- 加载上面定义的Common路径下面的类。