Skip to content

Java9 类加载新特性

JDK 9 带来的类加载变化

JDK 9 引入模块系统(Project Jigsaw/JPMS),对类加载机制产生了深远影响。运行时数据结构、方法区实现、类加载器的层次结构都发生了重大变化。

模块化系统(JPMS)

模块化的核心概念

模块化之前,JDK 是一个单一的类库。模块化之后,JDK 被拆分成了一系列模块:

JDK 8 及之前:
java.base     java.lang
java.sql      java.io
java.util     ...
(所有类都在同一个命名空间)

JDK 9 及之后:
java.base@9              java.sql@9
java.base/java/lang/     java.sql/java/sql/
java.base/java/io/        ...
java.base/java/util/      (每个模块有自己的命名空间)

模块路径 vs classpath

JDK 9 引入了模块路径的概念:

classpath(JDK 9 之前):
  -classpath xxx.jar:yyy.jar:...

module path(JDK 9+):
  --module-path out/production:lib/module1.jar:...

classpath:JAR 包平铺,所有类在同一命名空间

module path:模块化 JAR(Module JAR),有明确的模块边界

模块描述符:module-info.java

java
module com.example.myapp {
    // 依赖的模块
    requires com.example.lib;

    // 导出的包
    exports com.example.api;

    // 提供的服务
    provides MyService with MyServiceImpl;

    // 使用的服务
    uses MyService;
}

类加载器的变化

JDK 8 的类加载器层次结构

Bootstrap ClassLoader (C++)


Extension ClassLoader (sun.misc.Launcher$ExtClassLoader)


Application ClassLoader (sun.misc.Launcher$AppClassLoader)


自定义 ClassLoader

JDK 9 的类加载器层次结构

Platform ClassLoader (JDK 9 新增,替代 Extension ClassLoader)


Application ClassLoader


自定义 ClassLoader

关键变化:Extension ClassLoader 被替换为 Platform ClassLoader。

Platform ClassLoader

Platform ClassLoader 不再局限于 jre/lib/ext 目录,而是加载平台相关的 API:

java
// JDK 9 之前:java.util.Map 的实现类
// 由 Bootstrap ClassLoader 加载

// JDK 9 之后:java.base 模块中的类
// 由 Bootstrap ClassLoader 加载

// JDK 9 之后:java.sql 模块中的类
// 由 Platform ClassLoader 加载

Platform ClassLoader 加载的模块包括:

java.sql    java.xml.crypto
java.desktop java.naming
java.management ...

Platform ClassLoader vs Extension ClassLoader

特性Extension ClassLoader(JDK 8)Platform ClassLoader(JDK 9+)
加载范围jre/lib/ext 目录的 JARJDK 内部模块
命名sun.misc.Launcher$ExtClassLoaderjdk.internal.loader.PlatformClassLoader
位置ExtensionPlatform
父加载器BootstrapBootstrap

方法区的变化:Metaspace

JDK 8 的方法区

JDK 8 方法区(HotSpot):

  ├── 字符串常量池(StringTable)
  ├── 类元数据(Class 元信息)
  │      │
  │      ├── 运行时常量池
  │      ├── 字段信息
  │      ├── 方法信息
  │      └── ...
  └── 静态变量

  └── → 在堆中,受 -Xms/-Xmx 控制

JDK 9+ 的 Metaspace

JDK 9 把方法区移到了本地内存(Native Memory):

JDK 9+ 方法区(元空间 Metaspace):

  ├── 类元数据(Class 元信息)
  │      │
  │      ├── 运行时常量池
  │      ├── 字段信息
  │      ├── 方法信息
  │      └── ...

  └── → 在本地内存中,不受 -Xmx 限制

  └── MetaspaceSize 由 -XX:MetaspaceSize 控制

Metaspace 的调优

bash
# JDK 9+ Metaspace 调优参数
-XX:MetaspaceSize=128m      # 初始元空间大小
-XX:MaxMetaspaceSize=256m   # 最大元空间大小
-XX:MinMetaspaceFreeRatio=40   # 最小空闲比例
-XX:MaxMetaspaceFreeRatio=70   # 最大空闲比例

Metaspace vs Heap

特性HeapMetaspace
位置JVM 堆内存本地内存
控制参数-Xms / -Xmx-XX:MetaspaceSize
OOMHeapDumpOutOfMemoryErrorMetaspace OutOfMemoryError
GCFull GC 会回收卸载类时回收

类加载器命名空间的变化

JDK 9 之前的类加载器命名空间

Bootstrap ClassLoader 的命名空间
  └── java.*, javax.*, sun.*, com.sun.*, ...

Extension ClassLoader 的命名空间
  └── jre/lib/ext/*.jar 中的类

Application ClassLoader 的命名空间
  └── classpath 中的类

JDK 9+ 的类加载器命名空间

Bootstrap ClassLoader 的命名空间
  └── java.base 中的类

Platform ClassLoader 的命名空间
  └── java.sql, java.xml, java.desktop 等模块中的类

Application ClassLoader 的命名空间
  └── 用户代码(classpath/module-path)

模块化带来的类加载变化

模块系统对类加载的影响

java
// 模块化后的类可见性
// 模块 A 导出 com.example.api 包
// 模块 B 可以访问 com.example.api
// 模块 C(不依赖 A)不能访问 com.example.api
模块可见性规则:
1. 一个模块导出(exports)的包,只有依赖它的模块才能访问
2. 模块之间通过 requires 声明依赖
3. 没有通过 requires 导出的包,对其他模块不可见

模块化对反射的影响

JDK 9 限制了深度反射:

java
// JDK 9 之前:深度反射可以访问 private 成员
field.setAccessible(true);

// JDK 9+:深度反射受限(强封装)
// add-opens 和 add-exports 标志允许访问受限成员
--add-opens java.base/java.lang=ALL-UNNAMED
--add-exports java.base/java.lang=ALL-UNNAMED

模块化对类加载器的影响

java
// JDK 9 之后,ClassLoader 的子类要小心
// PlatformClassLoader 不再是 ExtClassLoader
// ExtClassLoader 在 JDK 9 中已不存在

服务加载:ServiceLoader 的变化

JDK 8 的 ServiceLoader

java
// JDK 8
ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
// Driver.class 由 Bootstrap 加载
// 但实现类由 TCCL 加载

JDK 9 的 ServiceLoader

java
// JDK 9
ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class, moduleLayer.classLoader());
// 使用模块层的类加载器

ServiceLoader 在 JDK 9 中支持按模块加载服务实现。

运行时变化

JVM 参数变化

bash
# JDK 8 及之前
-Xms256m -Xmx1024m   # 控制堆大小(包括方法区)

# JDK 9+
-Xms256m -Xmx1024m   # 只控制堆大小
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m  # 控制元空间

OOM 变化

JDK 8 及之前:
  - OutOfMemoryError: Java heap space(方法区在堆中)
  - OutOfMemoryError: PermGen space(永久代溢出)

JDK 9+:
  - OutOfMemoryError: Java heap space(只有堆)
  - OutOfMemoryError: Metaspace(Metaspace 溢出)

字符串常量池的变化

JDK 9 把字符串常量池从方法区移到了堆中:

JDK 8 及之前:
  方法区 → 包含字符串常量池

JDK 9+:
  Metaspace → 只包含类元数据
  堆 → 包含字符串常量池

本节小结

JDK 9 类加载新特性核心要点:

特性说明
模块化系统JDK 9 引入 JPMS,类库被拆分成模块
模块路径替代/补充 classpath,支持模块化 JAR
Platform ClassLoader替代 Extension ClassLoader
Metaspace方法区移至本地内存,不再占用堆
Metaspace 调优-XX:MetaspaceSize / -XX:MaxMetaspaceSize
强封装JDK 9+ 限制深度反射,需要 --add-opens
ServiceLoader支持按模块加载
字符串常量池从方法区移到堆中

到这里,「字节码与类加载深度剖析」部分全部完成。接下来你可以继续学习 JVM 性能监控与调优,或者回顾 字节码指令集类加载机制

基于 VitePress 构建