类加载器
在jvm中类加载器是有等级之分的,从高到低分别是:根加载器/启动类加载器(Bootstrp loader),扩展类加载器(ExtClassLoader),应用程序类加载器(AppClassLoader/Application ClassLoader),在下面还有自定义加载器。
- 根加载器用于加载核心类库/本地方法类(
%JAVA_HOME%/lib目录下的rt.jar、resources.jar、charsets.jar等 jar 包和类) - 扩展类加载器用于加载JDK内部实现的扩展类。
%JRE_HOME%/lib/ext目录下的 jar 包和类以及被java.ext.dirs系统变量所指定的路径下的所有类。 - 应用程序类加载器加载程序中的文件,也就是面向我们用户,它负责加载用户类路径(ClassPath)上所指定的类库。
- 自定义加载器用于满足自己的特殊需求。
双亲委派机制
有这么多类加载器,我们应该如何选择加载器来加载我们的类呢?这时我们就需要使用双亲委派机制了。
应用程序是由三种类加载器互相配合从而实现类加载,除此之外还可以加入自己定义的类加载器。
双亲委派模型要求除了顶层的启动类加载器外,其它的类加载器都要有自己的父类加载器。这里的父子关系一般通过组合关系(Composition)来实现,而不是继承关系(Inheritance)。并且父子类关系就行我上面说的类加载器一样。
工作流程:一个类加载器会将收到的类加载请求发送到自己的父类加载器,一直到传送到顶级加载器。只有父类加载器无法加载该类才会交给子类去处理。
这么做的好处:使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系,从而使得基础类得到统一。
类加载器之间的父子关系一般不是以继承的关系来实现的,而是通常使用组合关系来复用父加载器的代码。
简单总结一下双亲委派模型的执行流程:
- 在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载(每个父类加载器都会走一遍这个流程)。
- 类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父加载器
loadClass()方法来加载类)。这样的话,所有的请求最终都会传送到顶层的启动类加载器BootstrapClassLoader中。 - 只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载(调用自己的
findClass()方法来加载类)。
打破双亲委派
双亲委派机制实现向上委派是通过调用父加载器 loadClass()方法来加载类来实现的。所以要打破我们就可以重写该方法来实现。
Tomcat 服务器为了能够优先加载 Web 应用目录下的类,然后再加载其他目录下的类,就自定义了类加载器 WebAppClassLoader 来打破双亲委托机制。这也是 Tomcat 下 Web 应用之间的类实现隔离的具体原理。
著作权归所有 原文链接:javaguide.cn/java/jvm/cl… ;
《对线面试官》Java编译到执行的过程 (qq.com);
CS-Notes/Java 虚拟机.md at master · CyC2018/CS-Notes (github.com)