当前位置: 首页 > news >正文

JVM 的启动器类解读 -- sun.misc.Launcher

Launcher 类解读

  • sun.misc.Launcher 类解读
    • 前置知识铺垫
      • 什么是类加载器(ClassLoader)
        • 类加载的过程
        • 类加载器的职责
      • 双亲委派模型(Parent Delegation Model)
        • 双亲委派机制流程
        • 为什么需要双亲委派?
      • 三大核心类加载器
      • JVM 启动流程(简化版)
    • Launcher 类的核心作用
      • 介绍
      • 核心设计模式
    • 核心源码详细解读
      • Launcher 的核心字段
      • 构造函数 - 核心初始化流程
        • 关键点解析
      • ExtClassLoader - 扩展类加载器
        • 类定义和继承关系
        • 创建 ExtClassLoader
        • 获取扩展目录
        • 构造器 - 注意父加载器为 null
        • 将目录转换为 URL 数组
        • 查找本地库
      • AppClassLoader - 应用程序类加载器
        • 类定义
        • 创建 AppClassLoader
        • 构造器
        • 重写 loadClass - 性能优化
        • 权限管理
        • 运行时添加类路径(Instrumentation)
      • BootClassPathHolder - 启动类路径持有者
      • 工具方法
        • 路径转 URL 数组
        • 解析类路径字符串
        • 文件转 URL
      • Factory - URL 协议处理器工厂
      • PathPermissions - 路径权限管理
    • 上下游关系图解
      • JVM 启动时的类加载器创建流程
      • 类加载器的层次结构
      • 双亲委派模型的类加载流程
      • Launcher 与其他关键类的协作关系
      • 类加载过程中的关键方法调用链
    • 核心知识点总结
      • Launcher 的五大职责
      • 三大类加载器对比
      • 5.3 关键设计模式
      • 线程上下文类加载器的作用
      • 性能优化技巧
        • knownToNotExist 缓存
        • MetaIndex 目录索引
        • 并行类加载
    • 实际应用场景
      • JDBC 驱动加载(SPI 机制)
      • Tomcat 类加载器(自定义类加载器)
    • 总结
      • 核心价值

sun.misc.Launcher 类解读

前置知识铺垫

什么是类加载器(ClassLoader)

在深入理解 Launcher 之前,我们需要先了解 Java 的类加载机制:

类加载的过程
加载(Loading)↓
链接(Linking)├─ 验证(Verification)├─ 准备(Preparation)└─ 解析(Resolution)↓
初始化(Initialization)
类加载器的职责
  • 负责将 .class 字节码文件加载到 JVM 内存
  • 将字节码转换为 java.lang.Class 对象
  • 通过全限定类名来定位并加载类

双亲委派模型(Parent Delegation Model)

这是理解 Launcher 的核心前置知识:

BootstrapClassLoader(启动类加载器)C++ 实现↑ 父加载器|
ExtClassLoader(扩展类加载器)↑ 父加载器|
AppClassLoader(应用程序类加载器)↑ 父加载器|
自定义ClassLoader(用户自定义类加载器)
双亲委派机制流程
1. 收到类加载请求↓
2. 先委派给父加载器尝试加载↓
3. 父加载器找不到?├─ 是 → 自己尝试加载└─ 否 → 返回父加载器加载的类
为什么需要双亲委派?

保证 Java 核心类库的安全性

// 假设你自己写了一个 java.lang.String 类
package java.lang;public class String {// 恶意代码public void maliciousMethod() {// 危险操作}
}

由于双亲委派机制:

  1. 你的应用要加载 java.lang.String
  2. AppClassLoader 委派给 ExtClassLoader
  3. ExtClassLoader 委派给 BootstrapClassLoader
  4. BootstrapClassLoader 已经加载了 JDK 核心的 String
  5. 返回 JDK 的 String,你的恶意类永远不会被加载

三大核心类加载器

类加载器实现语言负责加载的路径父加载器
BootstrapClassLoaderC++$JAVA_HOME/jre/lib (如 rt.jar)
ExtClassLoaderJava$JAVA_HOME/jre/lib/extBootstrapClassLoader
AppClassLoaderJavaCLASSPATH / java.class.pathExtClassLoader

JVM 启动流程(简化版)

1. 操作系统加载 JVM(C++ 编写)↓
2. JVM 初始化 BootstrapClassLoader(C++ 实现)↓
3. BootstrapClassLoader 加载核心类库(rt.jar)↓
4. 创建 sun.misc.Launcher 实例 ← 我们今天的主角↓
5. Launcher 创建 ExtClassLoader↓
6. Launcher 创建 AppClassLoader↓
7. 设置线程上下文类加载器为 AppClassLoader↓
8. 使用 AppClassLoader 加载主类(main 方法所在的类)↓
9. 执行 main 方法,程序开始运行

Launcher 类的核心作用

介绍

sun.misc.LauncherJVM 启动应用程序的入口类,它的核心职责是:

  1. 创建并初始化扩展类加载器(ExtClassLoader)
  2. 创建并初始化应用程序类加载器(AppClassLoader)
  3. 设置线程上下文类加载器
  4. 管理安全管理器(SecurityManager)
  5. 提供访问 Bootstrap ClassPath 的能力

核心设计模式

这个类里面我们可以学习到很多设计模式的使用,整体看下来感觉挺受用的.

public class Launcher {// 单例模式 - 全局唯一的 Launcher 实例private static Launcher launcher = new Launcher();// 工厂模式 - URLStreamHandlerFactoryprivate static URLStreamHandlerFactory factory = new Factory();// 懒加载模式 - BootClassPathHolder 静态内部类private static class BootClassPathHolder {static final URLClassPath bcp;// ...}
}

核心源码详细解读

Launcher 的核心字段

public class Launcher {// URL 协议处理器工厂(如 http、https、file 等协议的处理)private static URLStreamHandlerFactory factory = new Factory();// 单例 - JVM 全局唯一的 Launcher 实例private static Launcher launcher = new Launcher();// 启动类路径(Bootstrap ClassPath)// 例如:/usr/lib/jvm/java-8/jre/lib/rt.jarprivate static String bootClassPath =System.getProperty("sun.boot.class.path");// 应用程序类加载器的引用private ClassLoader loader;// 获取全局唯一的 Launcher 实例public static Launcher getLauncher() {return launcher;}
}

构造函数 - 核心初始化流程

这是整个类加载体系的初始化入口:

public Launcher() {// ===========================================// 步骤1:创建扩展类加载器(ExtClassLoader)// ===========================================ClassLoader extcl;try {// 调用静态工厂方法创建扩展类加载器extcl = ExtClassLoader.getExtClassLoader();} catch (IOException e) {throw new InternalError("Could not create extension class loader", e);}// ===========================================// 步骤2:创建应用程序类加载器(AppClassLoader)// 注意:extcl 作为参数传入,成为 AppClassLoader 的父加载器// ===========================================try {// 传入 extcl 作为父加载器loader = AppClassLoader.getAppClassLoader(extcl);} catch (IOException e) {throw new InternalError("Could not create application class loader", e);}// ===========================================// 步骤3:设置主线程的上下文类加载器// ===========================================// 这一步非常关键!为 SPI 机制埋下伏笔// 后续 ServiceLoader 会通过这个上下文类加载器加载服务实现类Thread.currentThread().setContextClassLoader(loader);// ===========================================// 步骤4:安装安全管理器(如果需要)// ===========================================String s = System.getProperty("java.security.manager");if (s != null) {SecurityManager sm = null;if ("".equals(s) || "default".equals(s)) {// 使用默认的安全管理器sm = new java.lang.SecurityManager();} else {// 使用自定义的安全管理器类try {// 使用 AppClassLoader 加载自定义 SecurityManagersm = (SecurityManager)loader.loadClass(s).newInstance();} catch (IllegalAccessException e) {} catch (InstantiationException e) {} catch (ClassNotFoundException e) {} catch (ClassCastException e) {}}if (sm != null) {System.setSecurityManager(sm);} else {throw new InternalError("Could not create SecurityManager: " + s);}}
}
关键点解析

这里需要注意两点!!!

  1. 为什么先创建 ExtClassLoader?

    • 因为 AppClassLoader 需要 ExtClassLoader 作为父加载器
    • 这样才能实现双亲委派模型
  2. 为什么要设置线程上下文类加载器?

    Thread.currentThread().setContextClassLoader(loader);
    

    这是为了解决 双亲委派模型的局限性

    • 问题场景:BootstrapClassLoader 加载的 JDBC 接口(java.sql.Driver
    • 需要加载:AppClassLoader 路径下的驱动实现类(如 com.mysql.cj.jdbc.Driver
    • 矛盾:父类加载器无法访问子类加载器加载的类
    • 解决方案:通过线程上下文类加载器(AppClassLoader)实现反向类加载

ExtClassLoader - 扩展类加载器

类定义和继承关系
static class ExtClassLoader extends URLClassLoader {static {// 注册为支持并行加载的类加载器// 这样多个线程可以同时使用此加载器加载不同的类ClassLoader.registerAsParallelCapable();}// ...
}
创建 ExtClassLoader
public static ExtClassLoader getExtClassLoader() throws IOException {// 1. 获取扩展目录列表final File[] dirs = getExtDirs();try {// 2. 在特权块中创建 ExtClassLoaderreturn AccessController.doPrivileged(new PrivilegedExceptionAction<ExtClassLoader>() {public ExtClassLoader run() throws IOException {int len = dirs.length;// 为每个扩展目录注册元数据索引(优化类查找速度)for (int i = 0; i < len; i++) {MetaIndex.registerDirectory(dirs[i]);}// 创建 ExtClassLoader 实例return new ExtClassLoader(dirs);}});} catch (java.security.PrivilegedActionException e) {throw (IOException) e.getException();}
}
获取扩展目录
private static File[] getExtDirs() {// 从系统属性获取扩展目录路径// 例如:/usr/lib/jvm/java-8/jre/lib/extString s = System.getProperty("java.ext.dirs");File[] dirs;if (s != null) {// 使用平台相关的路径分隔符分割多个目录// Windows: ';'   Unix/Linux: ':'StringTokenizer st =new StringTokenizer(s, File.pathSeparator);int count = st.countTokens();dirs = new File[count];for (int i = 0; i < count; i++) {dirs[i] = new File(st.nextToken());}} else {dirs = new File[0];}return dirs;
}
构造器 - 注意父加载器为 null
public ExtClassLoader(File[] dirs) throws IOException {// 【重点】第二个参数 parent = null// 这意味着 ExtClassLoader 的父加载器是 null// 但在双亲委派时,会委派给 BootstrapClassLoader(C++ 实现,Java 中表示为 null)super(getExtURLs(dirs), null, factory);// 初始化查找缓存(优化性能)SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
将目录转换为 URL 数组
private static URL[] getExtURLs(File[] dirs) throws IOException {Vector<URL> urls = new Vector<URL>();for (int i = 0; i < dirs.length; i++) {// 列出扩展目录中的所有文件String[] files = dirs[i].list();if (files != null) {for (int j = 0; j < files.length; j++) {// 跳过 meta-index 文件if (!files[j].equals("meta-index")) {File f = new File(dirs[i], files[j]);// 将 jar 文件路径转换为 URLurls.add(getFileURL(f));}}}}URL[] ua = new URL[urls.size()];urls.copyInto(ua);return ua;
}
查找本地库
public String findLibrary(String name) {// 将库名转换为平台相关的名称// 例如:Windows -> xxx.dll, Linux -> libxxx.soname = System.mapLibraryName(name);URL[] urls = super.getURLs();File prevDir = null;for (int i = 0; i < urls.length; i++) {// 获取扩展目录File dir = new File(urls[i].getPath()).getParentFile();if (dir != null && !dir.equals(prevDir)) {// 1. 先查找架构相关的子目录(如 amd64、x86)String arch = VM.getSavedProperty("os.arch");if (arch != null) {File file = new File(new File(dir, arch), name);if (file.exists()) {return file.getAbsolutePath();}}// 2. 再查找扩展目录本身File file = new File(dir, name);if (file.exists()) {return file.getAbsolutePath();}}prevDir = dir;}return null;
}

AppClassLoader - 应用程序类加载器

类定义
static class AppClassLoader extends URLClassLoader {static {// 支持并行类加载ClassLoader.registerAsParallelCapable();}// URLClassPath 用于高效查找类文件final URLClassPath ucp;// ...
}
创建 AppClassLoader
public static ClassLoader getAppClassLoader(final ClassLoader extcl)throws IOException {// 1. 从系统属性获取 CLASSPATH// 例如:/home/user/app.jar:/home/user/lib/*final String s = System.getProperty("java.class.path");final File[] path = (s == null) ? new File[0] : getClassPath(s);// 2. 在特权块中创建 AppClassLoaderreturn AccessController.doPrivileged(new PrivilegedAction<AppClassLoader>() {public AppClassLoader run() {URL[] urls =(s == null) ? new URL[0] : pathToURLs(path);// 【重点】extcl 作为父加载器传入return new AppClassLoader(urls, extcl);}});
}
构造器
AppClassLoader(URL[] urls, ClassLoader parent) {// 【重点】parent 参数 = ExtClassLoader// 这样就建立了双亲委派的层次结构super(urls, parent, factory);// 获取并初始化 URLClassPath(用于高效查找类)ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);ucp.initLookupCache(this);
}
重写 loadClass - 性能优化

这是一个非常巧妙的性能优化:

public Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {// 1. 安全检查(如果有 SecurityManager)int i = name.lastIndexOf('.');if (i != -1) {SecurityManager sm = System.getSecurityManager();if (sm != null) {// 检查包访问权限sm.checkPackageAccess(name.substring(0, i));}}// 2. 【核心优化】使用缓存判断类是否已知不存在if (ucp.knownToNotExist(name)) {// 这个类在父加载器和本地 URLClassPath 中都不存在// 检查是否已经被动态定义过Class<?> c = findLoadedClass(name);if (c != null) {if (resolve) {resolveClass(c);}return c;}// 直接抛出异常,避免走完整的双亲委派流程throw new ClassNotFoundException(name);}// 3. 正常走双亲委派流程return (super.loadClass(name, resolve));
}

性能优化的原理:

正常流程(没有优化):
ClassNotFoundException↓
委派给 ExtClassLoader↓
委派给 BootstrapClassLoader↓
BootstrapClassLoader 查找失败↓
ExtClassLoader 查找失败↓
AppClassLoader 查找失败↓
抛出 ClassNotFoundException优化后的流程:
查询 knownToNotExist 缓存↓
命中缓存,已知不存在↓
直接抛出 ClassNotFoundException
(跳过所有父加载器的查找过程)
权限管理
protected PermissionCollection getPermissions(CodeSource codesource) {PermissionCollection perms = super.getPermissions(codesource);// 允许从 classpath 加载的所有类调用 System.exit()perms.add(new RuntimePermission("exitVM"));return perms;
}
运行时添加类路径(Instrumentation)
private void appendToClassPathForInstrumentation(String path) {assert(Thread.holdsLock(this));// 将新路径添加到类路径// 如果路径已存在,addURL 是空操作// 这个方法供 java.lang.instrument.Instrumentation 使用super.addURL(getFileURL(new File(path)));
}

BootClassPathHolder - 启动类路径持有者

这是一个 懒加载 的静态内部类:

private static class BootClassPathHolder {static final URLClassPath bcp;static {URL[] urls;if (bootClassPath != null) {// 在特权块中处理 bootClassPathurls = AccessController.doPrivileged(new PrivilegedAction<URL[]>() {public URL[] run() {File[] classPath = getClassPath(bootClassPath);int len = classPath.length;Set<File> seenDirs = new HashSet<File>();for (int i = 0; i < len; i++) {File curEntry = classPath[i];// 对于 jar 文件,注册其父目录if (!curEntry.isDirectory()) {curEntry = curEntry.getParentFile();}if (curEntry != null && seenDirs.add(curEntry)) {// 注册目录以优化查找MetaIndex.registerDirectory(curEntry);}}return pathToURLs(classPath);}});} else {urls = new URL[0];}// 创建 URLClassPath 包装器bcp = new URLClassPath(urls, factory);bcp.initLookupCache(null);}
}// 公共访问方法
public static URLClassPath getBootstrapClassPath() {// 第一次调用时才会初始化 BootClassPathHolderreturn BootClassPathHolder.bcp;
}

懒加载的好处:

  • 只有在需要访问 BootstrapClassPath 时才初始化
  • 避免不必要的资源消耗
  • 线程安全(静态初始化块由 JVM 保证线程安全)

工具方法

路径转 URL 数组
private static URL[] pathToURLs(File[] path) {URL[] urls = new URL[path.length];for (int i = 0; i < path.length; i++) {urls[i] = getFileURL(path[i]);}return urls;
}
解析类路径字符串
private static File[] getClassPath(String cp) {File[] path;if (cp != null) {int count = 0, maxCount = 1;int pos = 0, lastPos = 0;// 第一遍:统计路径分隔符的数量while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {maxCount++;lastPos = pos + 1;}path = new File[maxCount];lastPos = pos = 0;// 第二遍:分割路径while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {if (pos - lastPos > 0) {path[count++] = new File(cp.substring(lastPos, pos));} else {// 空路径组件转换为 "."(当前目录)path[count++] = new File(".");}lastPos = pos + 1;}// 处理最后一个路径组件if (lastPos < cp.length()) {path[count++] = new File(cp.substring(lastPos));} else {path[count++] = new File(".");}// 裁剪数组到实际大小if (count != maxCount) {File[] tmp = new File[count];System.arraycopy(path, 0, tmp, 0, count);path = tmp;}} else {path = new File[0];}return path;
}
文件转 URL
static URL getFileURL(File file) {try {// 转换为规范路径(解析 . 和 .. 等)file = file.getCanonicalFile();} catch (IOException e) {}try {// 使用 ParseUtil 将文件路径编码为 URL// 例如:/home/user/app.jar -> file:/home/user/app.jarreturn ParseUtil.fileToEncodedURL(file);} catch (MalformedURLException e) {throw new InternalError(e);}
}

Factory - URL 协议处理器工厂

private static class Factory implements URLStreamHandlerFactory {private static String PREFIX = "sun.net.www.protocol";public URLStreamHandler createURLStreamHandler(String protocol) {// 根据协议名称构造处理器类名// 例如:http -> sun.net.www.protocol.http.HandlerString name = PREFIX + "." + protocol + ".Handler";try {Class<?> c = Class.forName(name);return (URLStreamHandler)c.newInstance();} catch (ReflectiveOperationException e) {throw new InternalError("could not load " + protocol +" system protocol handler", e);}}
}

支持的协议示例:

  • httpsun.net.www.protocol.http.Handler
  • httpssun.net.www.protocol.https.Handler
  • filesun.net.www.protocol.file.Handler
  • jarsun.net.www.protocol.jar.Handler
  • ftpsun.net.www.protocol.ftp.Handler

PathPermissions - 路径权限管理

这个类用于管理类加载器的文件访问权限:

class PathPermissions extends PermissionCollection {private File path[];          // 路径数组private Permissions perms;    // 权限集合URL codeBase;                 // 代码源PathPermissions(File path[]) {this.path = path;this.perms = null;        // 延迟初始化this.codeBase = null;}private synchronized void init() {if (perms != null)return;  // 已经初始化过perms = new Permissions();// 1. 添加创建类加载器的权限perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);// 2. 添加读取所有 "java.*" 系统属性的权限perms.add(new java.util.PropertyPermission("java.*",SecurityConstants.PROPERTY_READ_ACTION));// 3. 为每个路径添加文件读取权限AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {for (int i = 0; i < path.length; i++) {File f = path[i];String path;try {path = f.getCanonicalPath();} catch (IOException ioe) {path = f.getAbsolutePath();}if (i == 0) {codeBase = Launcher.getFileURL(new File(path));}if (f.isDirectory()) {// 目录:授予递归读取权限(-)if (path.endsWith(File.separator)) {perms.add(new FilePermission(path + "-",SecurityConstants.FILE_READ_ACTION));} else {perms.add(new FilePermission(path + File.separator + "-",SecurityConstants.FILE_READ_ACTION));}} else {// 文件:授予其所在目录的递归读取权限int endIndex = path.lastIndexOf(File.separatorChar);if (endIndex != -1) {path = path.substring(0, endIndex + 1) + "-";perms.add(new FilePermission(path,SecurityConstants.FILE_READ_ACTION));}}}return null;}});}public boolean implies(java.security.Permission permission) {if (perms == null)init();  // 懒加载return perms.implies(permission);}
}

上下游关系图解

JVM 启动时的类加载器创建流程

┌─────────────────────────────────────────────────────────────┐
│ JVM 启动(C++ 层)                                            │
└─────────────────┬───────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ 创建 BootstrapClassLoader (C++ 实现)                         │
│ 负责加载:$JAVA_HOME/jre/lib/rt.jar 等核心类库              │
└─────────────────┬───────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ BootstrapClassLoader 加载 sun.misc.Launcher 类               │
└─────────────────┬───────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ 执行 Launcher 的静态初始化                                   │
│   private static Launcher launcher = new Launcher();        │
└─────────────────┬───────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ Launcher 构造函数执行                                        │
└─────────────────┬───────────────────────────────────────────┘│├─────────────────────┐▼                     ▼┌──────────────────────┐  ┌──────────────────────┐│ 步骤1: 创建           │  │ 步骤2: 创建          ││ ExtClassLoader       │  │ AppClassLoader       ││                      │  │                      ││ ↓ getExtClassLoader()│  │ ↓ getAppClassLoader()││ ↓ getExtDirs()       │  │ ↓ getClassPath()     ││ ↓ 读取 java.ext.dirs │  │ ↓ 读取 java.class.path││ ↓ new ExtClassLoader │  │ ↓ new AppClassLoader ││   parent = null      │  │   parent = extcl     │└──────────────────────┘  └──────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ 步骤3: 设置线程上下文类加载器                                │
│   Thread.currentThread().setContextClassLoader(loader);     │
│   (loader = AppClassLoader)                                 │
└─────────────────┬───────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ 步骤4: 安装 SecurityManager(可选)                          │
└─────────────────┬───────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ 使用 AppClassLoader 加载用户主类                             │
│   loader.loadClass("com.example.Main")                      │
└─────────────────┬───────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ 执行主类的 main 方法                                         │
│   Main.main(String[] args)                                  │
└─────────────────────────────────────────────────────────────┘

类加载器的层次结构

┌────────────────────────────────────────────────────────────────┐
│                   BootstrapClassLoader                         │
│                   (C++ 实现, Java 中表示为 null)                │
│                                                                │
│  负责加载:                                                    │
│    • $JAVA_HOME/jre/lib/rt.jar                                │
│    • $JAVA_HOME/jre/lib/resources.jar                         │
│    • $JAVA_HOME/jre/lib/charsets.jar                          │
│    • 核心 Java 类库(java.*, javax.*, sun.* 等)              │
└────────────────────────┬───────────────────────────────────────┘│ 父加载器▼
┌────────────────────────────────────────────────────────────────┐
│                   ExtClassLoader                               │
│         (sun.misc.Launcher$ExtClassLoader)                     │
│                   extends URLClassLoader                       │
│                                                                │
│  负责加载:                                                    │
│    • $JAVA_HOME/jre/lib/ext/*.jar                             │
│    • java.ext.dirs 系统属性指定的目录                          │
│  示例:                                                        │
│    • /usr/lib/jvm/java-8/jre/lib/ext/cldrdata.jar            │
│    • /usr/lib/jvm/java-8/jre/lib/ext/sunec.jar               │
└────────────────────────┬───────────────────────────────────────┘│ 父加载器▼
┌────────────────────────────────────────────────────────────────┐
│                   AppClassLoader                               │
│         (sun.misc.Launcher$AppClassLoader)                     │
│                   extends URLClassLoader                       │
│                                                                │
│  负责加载:                                                    │
│    • CLASSPATH 环境变量指定的路径                              │
│    • java.class.path 系统属性指定的路径                        │
│    • -cp / -classpath 命令行参数指定的路径                     │
│  示例:                                                        │
│    • /home/user/myapp.jar                                     │
│    • /home/user/lib/*                                         │
│    • /home/user/classes/                                      │
└────────────────────────┬───────────────────────────────────────┘│ 父加载器▼
┌────────────────────────────────────────────────────────────────┐
│               自定义 ClassLoader                               │
│         (用户自定义, extends ClassLoader)                       │
│                                                                │
│  用途:                                                        │
│    • 加载网络资源                                              │
│    • 字节码加密/解密                                           │
│    • 热部署                                                    │
│    • OSGi 模块化                                               │
└────────────────────────────────────────────────────────────────┘

双亲委派模型的类加载流程

用户调用:Class.forName("com.example.MyClass")
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AppClassLoader.loadClass("com.example.MyClass")             │
│                                                             │
│ 1. 检查类是否已加载 (findLoadedClass)                       │
│    └─> 已加载?返回缓存的 Class 对象                        │
│                                                             │
│ 2. 委派给父加载器                                           │
│    └─> parent.loadClass("com.example.MyClass")             │
└─────────────────────────┬───────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ ExtClassLoader.loadClass("com.example.MyClass")             │
│                                                             │
│ 1. 检查类是否已加载 (findLoadedClass)                       │
│    └─> 已加载?返回缓存的 Class 对象                        │
│                                                             │
│ 2. 委派给父加载器                                           │
│    └─> parent.loadClass("com.example.MyClass")             │
└─────────────────────────┬───────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ BootstrapClassLoader.loadClass("com.example.MyClass")       │
│                                                             │
│ 在 $JAVA_HOME/jre/lib/ 中查找                               │
│ └─> 找不到 com.example.MyClass                             │
│ └─> 返回 null                                               │
└─────────────────────────┬───────────────────────────────────┘│ 父加载器找不到▼
┌─────────────────────────────────────────────────────────────┐
│ 回到 ExtClassLoader                                         │
│                                                             │
│ 3. 父加载器加载失败,自己尝试加载                           │
│    └─> findClass("com.example.MyClass")                    │
│    └─> 在 $JAVA_HOME/jre/lib/ext/ 中查找                   │
│    └─> 找不到 com.example.MyClass                          │
│    └─> 抛出 ClassNotFoundException                         │
└─────────────────────────┬───────────────────────────────────┘│ 自己也找不到▼
┌─────────────────────────────────────────────────────────────┐
│ 回到 AppClassLoader                                         │
│                                                             │
│ 3. 父加载器加载失败,自己尝试加载                           │
│    └─> findClass("com.example.MyClass")                    │
│    └─> 在 CLASSPATH 中查找                                 │
│    └─> 找到 /home/user/classes/com/example/MyClass.class   │
│    └─> 读取字节码并调用 defineClass                        │
│    └─> 返回 Class<MyClass> 对象 ✓                          │
└─────────────────────────────────────────────────────────────┘

Launcher 与其他关键类的协作关系

┌────────────────────────────────────────────────────────────┐
│                    JVM (C++ 层)                            │
└────────┬───────────────────────────────────────────────────┘│ 调用▼
┌────────────────────────────────────────────────────────────┐
│              sun.misc.Launcher                             │
│                                                            │
│  ┌──────────────────────────────────────────────────┐    │
│  │  private static Launcher launcher = new Launcher()│    │
│  └──────────────────────────────────────────────────┘    │
│                                                            │
│  创建    ┌──────────────────────┐                         │
│  ─────>  │  ExtClassLoader      │                         │
│          │  parent = null       │                         │
│          └──────────┬───────────┘                         │
│                     │                                      │
│  创建               │ 作为父加载器                         │
│  ─────>  ┌──────────▼───────────┐                         │
│          │  AppClassLoader      │                         │
│          │  parent = extcl      │                         │
│          └──────────────────────┘                         │
└────────────────────┬───────────────────────────────────────┘│▼
┌────────────────────────────────────────────────────────────┐
│           Thread (主线程)                                  │
│                                                            │
│  contextClassLoader = AppClassLoader                      │
│  (通过 Thread.currentThread().setContextClassLoader 设置) │
└────────────────────┬───────────────────────────────────────┘││ 被使用▼
┌────────────────────────────────────────────────────────────┐
│           ServiceLoader (SPI 机制)                         │
│                                                            │
│  public static <S> ServiceLoader<S> load(Class<S> service)│
│  {                                                         │
│      // 获取线程上下文类加载器(AppClassLoader)          │
│      ClassLoader cl =                                     │
│          Thread.currentThread().getContextClassLoader(); │
│      return ServiceLoader.load(service, cl);              │
│  }                                                         │
└────────────────────────────────────────────────────────────┘││ 实现双亲委派模型的"破坏"▼
┌────────────────────────────────────────────────────────────┐
│           DriverManager (JDBC)                             │
│                                                            │
│  static {                                                 │
│      // 在静态初始化块中使用 SPI 加载驱动                  │
│      ServiceLoader<Driver> loadedDrivers =                │
│          ServiceLoader.load(Driver.class);                │
│      // ...                                               │
│  }                                                         │
│                                                            │
│  场景说明:                                                │
│  • DriverManager 在 rt.jar 中,由 BootstrapClassLoader 加载│
│  • MySQL 驱动在 CLASSPATH 中,由 AppClassLoader 加载      │
│  • 通过线程上下文类加载器,父加载器可以访问子加载器资源    │
└────────────────────────────────────────────────────────────┘

类加载过程中的关键方法调用链

JVM 启动│├─> (C++) 创建 BootstrapClassLoader│├─> (C++) 加载 sun.misc.Launcher 类│     └─> BootstrapClassLoader.loadClass("sun.misc.Launcher")│├─> (Java) 执行 Launcher 静态初始化│     └─> private static Launcher launcher = new Launcher();│           ││           ├─> ExtClassLoader.getExtClassLoader()│           │     ││           │     ├─> getExtDirs()│           │     │     └─> System.getProperty("java.ext.dirs")│           │     ││           │     ├─> new ExtClassLoader(dirs)│           │     │     └─> super(getExtURLs(dirs), null, factory)│           │     │           └─> URLClassLoader 构造器│           │     ││           │     └─> 返回 ExtClassLoader 实例│           ││           ├─> AppClassLoader.getAppClassLoader(extcl)│           │     ││           │     ├─> System.getProperty("java.class.path")│           │     ││           │     ├─> getClassPath(s)│           │     │     └─> 解析 CLASSPATH 字符串│           │     ││           │     ├─> new AppClassLoader(urls, extcl)│           │     │     └─> super(urls, parent, factory)│           │     │           └─> URLClassLoader 构造器│           │     ││           │     └─> 返回 AppClassLoader 实例│           ││           ├─> Thread.currentThread().setContextClassLoader(loader)│           │     └─> 设置线程上下文类加载器为 AppClassLoader│           ││           └─> 安装 SecurityManager(可选)│├─> (C++) 使用 AppClassLoader 加载主类│     └─> AppClassLoader.loadClass("com.example.Main")│           ││           ├─> 检查类是否已加载│           │     └─> findLoadedClass("com.example.Main")│           ││           ├─> 委派给父加载器(双亲委派)│           │     └─> parent.loadClass("com.example.Main")│           │           └─> ExtClassLoader.loadClass(...)│           │                 └─> parent.loadClass(...)│           │                       └─> BootstrapClassLoader 查找失败│           │                             └─> 返回 null│           ││           ├─> 父加载器加载失败,自己尝试加载│           │     └─> findClass("com.example.Main")│           │           ││           │           ├─> URLClassLoader.findClass(...)│           │           │     ││           │           │     ├─> ucp.getResource(...)│           │           │     │     └─> 在 CLASSPATH 中查找 .class 文件│           │           │     ││           │           │     └─> defineClass(...)│           │           │           └─> 将字节码转换为 Class 对象│           │           ││           │           └─> 返回 Class<Main> 对象│           ││           └─> 返回 Class<Main> 对象│└─> (C++) 调用主类的 main 方法└─> Main.main(String[] args)└─> 用户程序开始运行

核心知识点总结

Launcher 的五大职责

职责说明代码位置
创建 ExtClassLoader负责加载扩展目录的 jar 包Launcher:72
创建 AppClassLoader负责加载 CLASSPATH 的类Launcher:81
设置上下文类加载器为 SPI 机制提供支持Launcher:88
管理安全管理器可选的安全检查机制Launcher:91-111
提供 Bootstrap ClassPath通过 BootClassPathHolder 提供访问Launcher:418

三大类加载器对比

特性BootstrapClassLoaderExtClassLoaderAppClassLoader
实现语言C++JavaJava
父加载器null (实际指向 Bootstrap)ExtClassLoader
加载路径$JAVA_HOME/jre/lib$JAVA_HOME/jre/lib/extCLASSPATH
系统属性sun.boot.class.pathjava.ext.dirsjava.class.path
典型加载类java.lang.Stringjavax.crypto.*用户应用类
Java 表示nullLauncher$ExtClassLoaderLauncher$AppClassLoader

5.3 关键设计模式

  1. 单例模式

    private static Launcher launcher = new Launcher();
    
  2. 工厂模式

    private static URLStreamHandlerFactory factory = new Factory();
    
  3. 懒加载模式

    private static class BootClassPathHolder {static final URLClassPath bcp;// 只在第一次调用时初始化
    }
    
  4. 双亲委派模式

    // URLClassLoader.loadClass 的默认实现
    protected Class<?> loadClass(String name, boolean resolve) {// 1. 检查缓存Class<?> c = findLoadedClass(name);if (c == null) {// 2. 委派给父加载器if (parent != null) {c = parent.loadClass(name, false);}// 3. 父加载器加载失败,自己尝试if (c == null) {c = findClass(name);}}return c;
    }
    

线程上下文类加载器的作用

问题场景:

BootstrapClassLoader (加载 java.sql.Driver 接口)↓ 需要加载
AppClassLoader (加载 com.mysql.cj.jdbc.Driver 实现类)

矛盾: 父类加载器无法访问子类加载器加载的类(违反双亲委派)

解决方案: 线程上下文类加载器

// Launcher.java:88
Thread.currentThread().setContextClassLoader(loader);// ServiceLoader.java:460
ClassLoader cl = Thread.currentThread().getContextClassLoader();

这样,BootstrapClassLoader 加载的 DriverManager 类就可以通过线程上下文类加载器(AppClassLoader)来加载 JDBC 驱动实现类。


性能优化技巧

knownToNotExist 缓存
// AppClassLoader.java:317
if (ucp.knownToNotExist(name)) {// 跳过双亲委派流程,直接抛出异常throw new ClassNotFoundException(name);
}

优化效果:

  • 避免重复的父加载器查找
  • 减少文件系统 I/O 操作
  • 对于已知不存在的类,立即失败
MetaIndex 目录索引
// ExtClassLoader.java:148
MetaIndex.registerDirectory(dirs[i]);

优化效果:

  • 预先建立目录索引
  • 快速判断类是否存在于某个 jar 包
  • 避免不必要的 jar 文件扫描
并行类加载
static {ClassLoader.registerAsParallelCapable();
}

优化效果:

  • 允许多线程同时加载不同的类
  • 提高多核 CPU 利用率
  • 减少类加载时的锁竞争

实际应用场景

JDBC 驱动加载(SPI 机制)

// DriverManager.java (由 BootstrapClassLoader 加载)
static {loadInitialDrivers();
}private static void loadInitialDrivers() {// 使用 SPI 加载驱动// 这里会用到线程上下文类加载器(AppClassLoader)ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);for (Driver driver : loadedDrivers) {// 加载 com.mysql.cj.jdbc.Driver 等实现类}
}

调用链:

BootstrapClassLoader (加载 DriverManager)└─> ServiceLoader.load(Driver.class)└─> Thread.currentThread().getContextClassLoader()└─> 返回 AppClassLoader└─> 加载 MySQL Driver (在 CLASSPATH 中)

Tomcat 类加载器(自定义类加载器)

Tomcat 为每个 Web 应用创建独立的类加载器:

BootstrapClassLoader↑
ExtClassLoader↑
AppClassLoader↑
Common ClassLoader (Tomcat 公共类)↑
WebApp ClassLoader1 (应用1) | WebApp ClassLoader2 (应用2)

打破双亲委派的原因:

  • 每个 Web 应用隔离(可以有不同版本的库)
  • 热部署(重新加载某个应用而不影响其他应用)

总结

sun.misc.Launcher 是 Java 类加载体系的核心,它的主要价值在于:

核心价值

  1. 建立类加载器层次结构

    • 创建 ExtClassLoader 和 AppClassLoader
    • 实现双亲委派模型
  2. 为 SPI 机制提供支持

    • 设置线程上下文类加载器
    • 允许父类加载器访问子类加载器资源
  3. 优化类加载性能

    • MetaIndex 索引机制
    • knownToNotExist 缓存
    • 并行类加载支持
  4. 安全机制

    • SecurityManager 管理
    • 权限控制(PathPermissions)
http://www.dtcms.com/a/507813.html

相关文章:

  • java Servlet 概念讲解 以及和Golang概念对比
  • CoAtNet:让卷积与注意力在所有数据规模上“联姻”,CNN+Transformer融合
  • 个人网站的建设流程博物馆网站做的好的
  • 中间件与CORS(基于fastapi)
  • 【Go】P8 Go 语言核心数据结构:深入解析切片 (Slice)
  • 使用Wireshark测试手机APP网络通信完整指南
  • 【AI论文】MemMamba:对状态空间模型中记忆模式的重新思考
  • 郴州建站扁平化网站后台
  • 请问做网站和编程哪个容易些网站建设一般的流程
  • 三地两中心架构介绍
  • Harmony鸿蒙开发0基础入门到精通Day01--JavaScript篇
  • CCIE好像越来越销声匿迹了......
  • 自己做ppt网站汕头网站制作哪里好
  • UVa 1344 Tian Ji The Horse Racing
  • 网站交换链接友情链接的作用网站地图制作
  • 【给服务器安装服务器安装nacos】
  • 影楼模板网站html5风格网站特色
  • Spark的Shuffle过程
  • 前端HTML常用基础标
  • 智能井盖传感器如何成为智慧城市“无声卫士”?
  • Django Web 开发系列(一):视图基础与 URL 路由配置全解析
  • 【python】在Django中,执行原生SQL查询
  • 5 个 Windows 故障排除工具
  • 云南网站建设招商交换友情链接的渠道
  • 在SCNet使用异构海光DCU 部署文心21B大模型报错HIP out of memory(未调通)
  • 嘉兴网站建设优化温州快速建站公司
  • 西安自助建站公司网站没有做404页面
  • 解决Vcenter告警datastore存储容量不足问题
  • 骆驼重链抗体免疫文库构建:从动物免疫到文库质控的关键技术解析
  • BearPi小熊派 鸿蒙开发入门笔记(1)