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)
↑
│
自定义 ClassLoaderJDK 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 目录的 JAR | JDK 内部模块 |
| 命名 | sun.misc.Launcher$ExtClassLoader | jdk.internal.loader.PlatformClassLoader |
| 位置 | Extension | Platform |
| 父加载器 | Bootstrap | Bootstrap |
方法区的变化: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
| 特性 | Heap | Metaspace |
|---|---|---|
| 位置 | JVM 堆内存 | 本地内存 |
| 控制参数 | -Xms / -Xmx | -XX:MetaspaceSize |
| OOM | HeapDumpOutOfMemoryError | Metaspace OutOfMemoryError |
| GC | Full 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 性能监控与调优,或者回顾 字节码指令集 和 类加载机制。
