类加载机制深度剖析

1、类的生命周期

1)类加载过程

Java文件经过Javac命令编译成class文件,Java类加载器将main类加载进入JVM虚拟机,主类在运行中用到的其他类时,会逐步加载这些类。jar包并不是一次性加载的,而是用到哪个类时才进行加载

2)加载的步骤

  • 加载:通过IO从磁盘中读取class文件,只有使用该类时才会加载该类的字节码文件
  • 验证:校验字节码文件的格式是否正确
  • 准备:给静态变量分配空间(在元空间分配)并设置默认值
  • 解析:将静态方法的符号引用替换为指向所存内存地址的指针或句柄的直接引用
  • 初始化:对静态变量赋值,执行静态代码块
clipboard.png

3)符号引用、直接引用

  • 符号引用:一个 java 文件会编译成一个class文件。在编译时,java 类并不知道所引用的类的实际地址,因此只能使用符号引用来代替(1.类的全限定名,2.字段名和属性,3.方法名和属性)。
  • 直接引用:直接指向目标的指针(指向方法区,Class 对象)、指向相对偏移量(指向堆区,Class 实例对象)或指向能间接定位到目标的句柄。

4)静态链接、动态链接

  • 静态链接:在Class文件格式的常量池中有大量的符号引用。字节码中的方法调用都是以常量池中指向方法的符号引用作为参数,类加载的解析阶段,对于静态方法会直接转换为直接引用,,这种转换称之为静态链接。
  • 动态链接:在运行时,每个栈帧(方法)中都保存着指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了方法调用过程中的动态链接。在运用时符号引用转换为直接引用(非静态方法,只有在运行时才能确定真正执行的方法,比如只有运行时才能知道真正是哪个子类的方法该被执行),这种转换称之为动态链接。


    clipboard.png

5)字符串常量池、class常量池、运行时常量池

  • 字符串常量池:堆中一个全局的Hash表,里面存的是字符串实例(在堆中创建)的引用,在JVM中只有一份,被所有类共享
  • class常量池:Java类文件被编译成class文件后,在class文件中class常量池,用来存放字面量和符号引用
  • 运行时常量池:当类加载到内存中后,JVM将class常量池的内容存放到运行时常量池中。将符号引用替换为直接引用

参考:https://cloud.tencent.com/developer/article/1450501

2、类加载器和双亲委派机制

1)类加载器

  • 启动类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar,charsets.jar等
  • 扩展类加载器:负责加载支撑JVM运行的为晕JRE的ext目录下的扩展类库
  • 应用程序加载器:负责加载ClassPath路径下的类包
  • 自定义加载器:负责加载用户自定义路径下的类包

2)双亲委派机制

类加载时,子加载器委派其父加载器优先加载,每个类加载器都是如此,只有父类加载器在自己搜索的范围内找不到指定类的时候,子类加载器才会尝试自己加载

clipboard.png

当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

为什么要设计双亲委派机制?

  • 沙箱安全机制:自己实现的类无法篡改核心API功能
  • 避免类的重复加载:当父类已加载过该类的话,子类无需再加载一遍,保证被加载类的唯一性

3、Tomcat打破双亲委派机制

tomcat部署多个应用程序时,多个应用都需要加载Spring包,一个需要加载4.0版本,一个需要加载5.0版本,如果采用原有的双亲委派机制,一个类只会加载一次,无法满足要求。

clipboard.png

1)黄色区域为tomcat第一部分自定义加载器,依旧采用双亲委派机制,因为只需要有一份,重复共用

  • CommonClassLoader:Tomcat最基本的类加载器,加载路径下的class可以被Tomcat容器本身和各个webapp访问
  • CatalinaClassLoader:Tomcat是私有的类加载器,加载路径下的class只能被Tomcat容器使用,对webapp不可见
  • SharedClassLoader:各个webapp共享的加载器,对各个webapp可见,对Tomcat容器不可见

2)绿色区域为tomcat第二部分自定义加载器,Tomcat启动时会被每一个webapp创建一个WebAppClassLoader加载器,WebAppClassLoader加载器不需要委托父类去加载,而且自己去加载war包下都class文件

JSP如何实现热部署?

Tomcat会为每一个JSP生成一个对应的JasperLoader类加载器,JasperLoader类加载器只加载自己的JSP,当JSP文件被修改时,Tomcat启动一个线程监听JSP文件的修改,如果发现JSP文件被修改了则会卸载当前的类加载器,重新生成一个JasperLoader类加载器去加载被修改的JSP

你可能感兴趣的:(类加载机制深度剖析)