目录

JVM双亲委派机制

目录

Java 虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要使用该类的时候才会将它的 class 文件加载到内存生成 class 对象。而且加载某个类的 class 文件时,Java 虚拟机采用的是双亲委派模式,即把请求交给父类处理,它是一种任务委派模式。

https://cloud.ccczg.site/202205271807048.png

工作过程:

  • 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
  • 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;
  • 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载。

优势:

  • 避免类的重复加载,JVM 中区分不同类,不仅仅是根据类名,相同的 class 文件被不同的 ClassLoader 加载就属于两个不同的类(比如,Java中的Object类,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,如果不采用双亲委派模型,由各个类加载器自己去加载的话,系统中会存在多种不同的 Object 类)
  • 保护程序安全,防止核心 API 被随意篡改,避免用户自己编写的类动态替换 Java 的一些核心类,比如我们自定义类:java.lang.String

在 JVM 中表示两个 class 对象是否为同一个类存在两个必要条件:

  • 类的完成类名必须一致,包括包名
  • 加载这个类的 ClassLoader(指ClassLoader实例对象)必须相同

破坏双亲委派模型:

  • 双亲委派模型并不是一个强制性的约束模型,而是 Java 设计者推荐给开发者的类加载器实现方式,可以“被破坏”,只要我们自定义类加载器,重写 loadClass() 方法,指定新的加载逻辑就破坏了,重写 findClass() 方法不会破坏双亲委派。
  • 双亲委派模型有一个问题:顶层 ClassLoader,无法加载底层 ClassLoader 的类。典型例子JNDI、JDBC,所以加入了线程上下文类加载器(Thread Context ClassLoader),可以通过Thread.setContextClassLoaser()设置该类加载器,然后顶层 ClassLoader 再使用 Thread.getContextClassLoader() 获得底层的 ClassLoader 进行加载。
  • Tomcat 中使用了自定 ClassLoader,并且也破坏了双亲委托机制。每个应用使用 WebAppClassloader 进行单独加载,他首先使用 WebAppClassloader 进行类加载,如果加载不了再委托父加载器去加载,这样可以保证每个应用中的类不冲突。每个tomcat中可以部署多个项目,每个项目中存在很多相同的class文件(很多相同的jar包),他们加载到 jvm 中可以做到互不干扰。
  • 利用破坏双亲委派来实现代码热替换(每次修改类文件,不需要重启服务)。因为一个 Class 只能被一个ClassLoader 加载一次,否则会报 java.lang.LinkageError。当我们想要实现代码热部署时,可以每次都new 一个自定义的 ClassLoader 来加载新的 Class文件。JSP 的实现动态修改就是使用此特性实现。