一、类加载机制:
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
二、类加载器:
启动类加载器(Bootstrap ClassLoader):负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。(注:仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)
扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>\lib\ext目录中的,或被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
应用程序类加载器(Application ClassLoader):负责加载用户路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,一般情况下该类加载是程序中默认的类加载器。
三、双亲委派机制:
双亲委派机制,是按照加载器的层级关系,逐层进行委派。
要加载一个类MyClass.class,从低层级到高层级一级一级委派,先由应用层加载器委派给扩展类加载器,再由扩展类委派给启动类加载器;启动类加载器载入失败,再由扩展类加载器载入,扩展类加载器载入失败,最后由应用类加载器载入,如果应用类加载器也找不到则抛出ClassNotFound异常。
双亲委派机制的工作流程:
启动类加载器(Bootstrap Class Loader):
这是最顶层的类加载器,负责加载Java核心类库(如
java.lang.*等)。它没有父类加载器,是JVM的一部分。
扩展类加载器(Extension Class Loader):
负责加载Java的扩展类库(位于
jre/lib/ext目录下的类)。它的父类加载器是启动类加载器。
应用类加载器(Application Class Loader):
负责加载应用程序类路径(ClassPath)下的类。
它的父类加载器是扩展类加载器。
双亲委派机制的加载步骤:
应用类加载器收到加载请求:
当应用类加载器收到加载
MyClass.class的请求时,它不会立即加载,而是先委派给其父类加载器(扩展类加载器)。
扩展类加载器收到委派请求:
扩展类加载器同样不会立即加载,而是继续委派给其父类加载器(启动类加载器)。
启动类加载器尝试加载:
启动类加载器尝试加载
MyClass.class。如果加载成功,则直接返回该类;如果加载失败,则返回null。
扩展类加载器尝试加载:
如果启动类加载器未能加载,扩展类加载器会尝试加载
MyClass.class。如果加载成功,则返回该类;如果加载失败,则返回null。
应用类加载器尝试加载:
如果扩展类加载器也未能加载,应用类加载器会尝试加载
MyClass.class。如果加载成功,则返回该类;如果加载失败,则抛出ClassNotFoundException。
双亲委派机制的优势:
避免重复加载:
通过委派机制,确保每个类只被加载一次,避免类的重复加载。
安全性:
防止用户自定义的类替换Java核心类库中的类(如
java.lang.String),确保核心类库的安全性。
类的唯一性:
确保同一个类在JVM中只有一个实例,避免类的冲突。
双亲委派机制的缺点:
灵活性不足:
在某些场景下(如热部署、模块化加载),双亲委派机制可能显得不够灵活。
类加载器的隔离性:
不同类加载器加载的类无法直接相互访问,可能导致类加载器之间的隔离问题。
总结:
双亲委派机制是Java类加载的核心机制,通过层级委派的方式确保类的唯一性和安全性。虽然它在某些场景下可能显得不够灵活,但仍然是Java类加载的基础设计。理解这一机制对于深入掌握Java类加载和JVM运行原理非常重要。