说一说JVM双亲委派机制与Tomcat
讲个故事:
以前,爱捣鼓的小明突然灵机一动,写出了下面的代码
package java.lang; public class String { //...复制真正String的其他方法 public boolean equals(Object anObject) { sendEmail(xxx); return equalsReal(anObject); } //... }
这样,只要引用java.lang.String
的人,小明能随时收到他的系统的相关信息,这简直是个天才的注意。然而实施的时候却发现,JVM并没有加载这个类。
这是为什么呢?
小明能想到的事情,JVM设计者也肯定能想到。
双亲委派模型
上述故事纯属瞎编,不过,这确实是以前JVM存在的一个问题,这几天看Tomcat源代码的时候,发现频繁出现ClassLoader
为什么要用这个东西呢?
想要解答这个问题,得先了解一个定义:双亲委派模型。
这个词第一次看见是在《深入理解JVM》中,目的也是为了解决上面所提出来的问题。
在JVM中,存在三种类型的类加载器:
- 启动类(Bootstrap)加载器: 用于加载本地(
Navicat
)代码类的加载器,它负责装入%JAVA_HOME%/lib
下面的类。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。 - 标准扩展(Extension)类加载器: 由
ExtClassLoader
实现,负责加载%JAVA_HOME/lib/ext%
或者系统变量java.ext.dir
(可使用System.out.println("java.ext.dir")查看
)指定的类加载到内存中 - 系统(System)类加载器: 由
AppClassLoader
实现,负责加载系统类(环境变量%CLASSPATH%
)指定,默认为当前路径的类加载到内存中。
除去以上三种外,还有一种比较特殊的线程上下文类加载器。存在于Thread
类中,一般使用方式为new Thread().getContextClassLoader()
可以看出来,三种类型的加载器负责不同的模块的加载。那怎么才能保证我所使用的String
就是JDK里面的String
呢?这就是双亲委派模型的功能了:
上面三种类加载器中,他们之间的关系为:
也就是Bootstrap ClassLoader
作为Extension ClassLoader
的父类,而Extension ClassLoader
作为Application ClassLoader
的父类,Application ClassLoader
是作为User ClassLoader
的父类的。
而双亲委派机制规定:当某个特定的类加载在接收到类加载的请求的时候,首先需要将加载任务委托给父类加载器,依次递归到顶层后,如果最高层父类能够找到需要加载的类,则成功返回,若父类无法找到相关的类,则依次传递给子类。
补充:
- 如果A类引用了B,则JVM将使用加载类A的加载器加载类B