JVM双亲委派模型
什么是类加载器?
在谈到双亲委派模型之前,必须要先说明什么是类加载器。对类加载器有个基本认识之后,才能对双亲委派模型有一个更加深入的了解。
加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。
两个类相等,需要类本身相等,并且使用同一个类加载器进行加载。这是因为每一个类加载器都拥有一个独立的类名称空间。
类加载器有三个类别:
- **启动类加载器(Bootstrap ClassLoader)**此类加载器负责将存放在
<JRE_HOME>\lib
目录中的,或者被-Xbootclasspath
参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar
,名字不符合的类库即使放在lib
目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给启动类加载器,直接使用null
代替即可。 - **扩展类加载器(Extension ClassLoader)**这个类加载器是由
ExtClassLoader(sun.misc.Launcher$ExtClassLoader)
实现的。它负责将<JAVA_HOME>/lib/ext
或者被java.ext.dir
系统变量所指定路径中的所有类库加载到内存中,开发者可以直接使用扩展类加载器。 - 应用程序类加载器(Application ClassLoader)这个类加载器是由
AppClassLoader(sun.misc.Launcher$AppClassLoader)
实现的。由于这个类加载器是ClassLoader
中的getSystemClassLoader()
方法的返回值,因此一般称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
什么是双亲委派模型?
应用程序是由三种类加载器互相配合从而实现类加载。下图展示了类加载器之间的层次关系,称为双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其它的类加载器都要有自己的父类加载器。这里的父子关系一般通过组合关系(Composition)来实现,而不是继承关系(Inheritance)。
双亲委派模型的工作过程是怎样的?
一个类加载器首先将类加载请求转发到父类加载器,只有当父类加载器无法完成时才尝试自己加载。
举一个简单的例子,当你想要加载一个自定义的类User,首先先将请求转发到父类Extension ClassLoader
,然后再转发到父类Bootstrap ClassLoader
。接着从Bootstrap ClassLoader
开始找起,如果没找到User类则到其子类加载器,直到找到为止。
为什么需要双亲委派模型?
双亲委派模型保证了Java程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类),也保证了 Java 的核心 API 不被篡改。如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现一些问题,比如我们编写一个称为 java.lang.Object 类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类。