一、类加载机制:

类的加载指的是将类的.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异常。

双亲委派机制的工作流程:

  1. 启动类加载器(Bootstrap Class Loader)

    • 这是最顶层的类加载器,负责加载Java核心类库(如java.lang.*等)。

    • 它没有父类加载器,是JVM的一部分。

  2. 扩展类加载器(Extension Class Loader)

    • 负责加载Java的扩展类库(位于jre/lib/ext目录下的类)。

    • 它的父类加载器是启动类加载器。

  3. 应用类加载器(Application Class Loader)

    • 负责加载应用程序类路径(ClassPath)下的类。

    • 它的父类加载器是扩展类加载器。


双亲委派机制的加载步骤:

  1. 应用类加载器收到加载请求

    • 当应用类加载器收到加载MyClass.class的请求时,它不会立即加载,而是先委派给其父类加载器(扩展类加载器)。

  2. 扩展类加载器收到委派请求

    • 扩展类加载器同样不会立即加载,而是继续委派给其父类加载器(启动类加载器)。

  3. 启动类加载器尝试加载

    • 启动类加载器尝试加载MyClass.class。如果加载成功,则直接返回该类;如果加载失败,则返回null

  4. 扩展类加载器尝试加载

    • 如果启动类加载器未能加载,扩展类加载器会尝试加载MyClass.class。如果加载成功,则返回该类;如果加载失败,则返回null

  5. 应用类加载器尝试加载

    • 如果扩展类加载器也未能加载,应用类加载器会尝试加载MyClass.class。如果加载成功,则返回该类;如果加载失败,则抛出ClassNotFoundException


双亲委派机制的优势:

  1. 避免重复加载

    • 通过委派机制,确保每个类只被加载一次,避免类的重复加载。

  2. 安全性

    • 防止用户自定义的类替换Java核心类库中的类(如java.lang.String),确保核心类库的安全性。

  3. 类的唯一性

    • 确保同一个类在JVM中只有一个实例,避免类的冲突。


双亲委派机制的缺点:

  1. 灵活性不足

    • 在某些场景下(如热部署、模块化加载),双亲委派机制可能显得不够灵活。

  2. 类加载器的隔离性

    • 不同类加载器加载的类无法直接相互访问,可能导致类加载器之间的隔离问题。


总结:

双亲委派机制是Java类加载的核心机制,通过层级委派的方式确保类的唯一性和安全性。虽然它在某些场景下可能显得不够灵活,但仍然是Java类加载的基础设计。理解这一机制对于深入掌握Java类加载和JVM运行原理非常重要。