0%

Java 类加载机制(一):类加载的基本机制和过程

1. 类加载器 ClassLoader 的作用是

  • 类加载器 ClassLoader 就是加载其他类的类
  • 类加载器负责将字节码文件加载到内存,创建 Class 对象

2. 自定义 ClassLoader 可以实现哪些功能

  • 热部署。在不重启 Java 程序的情况下,动态替换类的实现,比如 Java Web 开发中的 JSP 技术就利用自定义的 ClassLoader 实现修改 JSP 代码即生效;OSGI(Open Service Gateway Initiative) 框架使用自定义的 ClassLoader 实现动态更新
  • 应用的模块化和互相隔离。不同的 ClassLoader 可以加载相同的类但互相隔离、互不影响。Web 应用服务器如 Tomcat 利用这一点在一个程序中管理多个 Web 应用程序,每个 Web 应用使用自己的 ClassLoader ,这些 Web 应用互不干扰。OSGI 和 Java 9 利用这一点实现了一个动态模块化架构,每个模块有自己的 ClassLoader,不同模块可以互不干扰
  • 从不同地方灵活加载。系统默认的 ClassLoader 一般从本地的 .class 文件或 jar 文件中加载字节码文件,通过自定义的 ClassLoader,我们可以从共享的 Web 服务器、数据库、缓存服务器等其他地方加载字节码文件

3. 类加载的基本机制

  • 运行 Java 程序,就是执行 java 这个命令,指定包含 main() 方法的完整类名,以及一个 classpath,即类路径。类路径可以有多个,对于直接的 class 文件,路径是 class 文件的根目录;对于 jar 包,路径是 jar 包的完整名称(包括路径和 jar 包名)
  • Java 运行时,会根据类的完全限定名寻找并加载类,寻找的方式基本就是在系统类和指定的类路径中寻找。如果是 class 文件的根目录,则直接查看是否有对应的子目录及文件;如果是 jar 文件,则首先在内存中解压文件,然后再查看是否有对应的类
  • 负责加载类的类就是类加载器,它的输入是完全限定的类名,输出Class 对象。类加载器不是只有一个,一般程序运行时,都会有三个适用于 Java 9 之前,Java 9 引入了模块化,基本概念是类似的,但有一些变化)

4. Java 程序在运行时,三个类加载器分别是

  • 启动类加载器(Bootstrap ClassLoader)

    • 这个加载器是 JVM 实现的一部分,不是 Java 语言实现的,一般是 C++ 实现的
    • 它负责加载 Java 的基础类,主要是 <JAVA_HOME>/lib/rt.jar,我们日常用的 Java 类库比如 Spring、ArrayList 等都位于该包内
  • 扩展类加载器(Extension ClassLoader)

    • 这个加载器的实现类sun.misc.Launcher$ExtClassLoader
    • 它负责加载 Java 的一些扩展类,一般是 <JAVA_HOME>/lib/ext 目录中的 jar 包
  • 应用程序类加载器(Application ClassLoader)/系统类加载器(System ClassLoader)

    • 这个加载器的实现类sun.misc.Launcher$AppClassLoader
    • 它负责加载应用程序的类,包括自己写的和引入的第三方的类库,即所有在类路径中指定的类

5. 这三个类加载器的关系是

  • 这三个类加载器有一定的关系,可以认为是父子关系
  • Application ClassLoader 的父亲是 Extension ClassLoaderExtension ClassLoader 的父亲是 Bootstrap ClassLoader
  • 注意不是父子继承关系,而是父子委派关系。子 ClassLoader 有一个变量 parent 指向父 ClassLoader在子 ClassLoader 加载类时,一般会首先通过父 ClassLoader 加载

6. 类加载的基本过程是

  • 判断是否已经加载过了,如果加载过了,直接返回 Class 对象。一个类只会被一个 ClassLoader 加载一次
  • 如果没有被加载,先让父 ClassLoader 去加载,如果加载成功,返回得到的 Class 对象
  • 在父 ClassLoader 没有加载成功的前提下,自己尝试加载类
  • 一个程序运行时,会创建一个 Application ClassLoader在程序中用到 ClassLoader 的地方,如果没有指定,一般用的都是这个 ClassLoader。所以,这个 ClassLoader 也被称为系统类加载器(System ClassLoader

7. 类加载过程又称为什么模型

  • 双亲委派模型:即优先让父 ClassLoader 去加载

8. 为什么要先让父 ClassLoader 去加载呢

  • 这样可以避免 Java 类库被覆盖的问题,保证类加载的唯一性和一致性
    • 比如,用户程序也定义了一个类 java.lang.String,通过双亲委派,java.lang.String 只会被 Bootstrap ClassLoader 加载
    • 这就避免了自定义的 String 覆盖 Java 类库的定义

9. 双亲委派虽然是一般模型,但也有一些例外,比如说

  • 自定义的加载顺序:尽管不被建议,自定义的 ClassLoader 可以不遵从双亲委派这个约定。不过,即使不遵从,以 java 开头的类也不能被自定义类加载器加载,这是由 Java 的安全机制保证的,以避免混乱
  • 网状加载顺序:在 OSGI 框架和 Java 9 模块化系统中,类加载器之间的关系是一个网,每个模块有一个类加载器,不同模块之间可能有依赖关系。在一个模块加载一个类时,可能是从自己模块加载,也可能是委派给其他模块的类加载器加载
  • 父加载器委派给子加载器加载:典型的例子有 JNDI(Java Naming and Directory Interface) 服务,它是 Java 企业级应用中的一项服务
-------------------- 本文结束感谢您的阅读 --------------------