Java 9+无法访问sun.*包本质是JPMS强封装策略生效,解决核心是分场景选合规路径:优先用标准API替代(如VarHandle替代Unsafe、HttpClient替代sun.net.utils),必要时用--add-exports/--add-opens临时授权,慎用降级。

Java 9+ 模块化后无法访问 sun.* 包(如 sun.nio.ch.DirectBuffer、sun.net.utils、sun.misc.Unsafe 等),本质是 JPMS 的强封装策略生效:这些类属于 java.base 模块的内部实现,未导出、未开放,反射或直接引用均被拦截。解决核心不是“绕开限制”,而是分场景选择**合规路径**——优先迁移,必要时授权,慎用降级。
优先替换为标准 API
绝大多数 sun.* 功能已有稳定替代:
-
替代
sun.misc.Unsafe:使用java.util.concurrent.atomic工具类(如AtomicInteger)、VarHandle(JDK 9+)或jdk.internal.misc.Unsafe(仅限 JDK 内部模块,不可公开依赖) -
替代
sun.nio.ch.DirectBuffer:通过Buffer.isDirect()+Buffer.arrayOffset()/address()(需--add-opens)判断;更推荐用ByteBuffer.allocateDirect()创建后统一管理,避免直接操作底层地址 -
替代
sun.net.utils:HTTP 相关逻辑迁移到java.net.http.HttpClient(JDK 11+)或成熟库(OkHttp、Apache HttpClient);网络工具类可用InetAddress、NetworkInterface等标准类
运行时临时开放权限(仅限过渡)
若短期无法改造(如依赖未升级的第三方库),必须在 JVM 启动时显式授权:
- 对反射访问(如 Lombok、MapStruct、AOP 框架触发的
clone()、setAccessible()):用--add-opens
java --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED -jar app.jar - 对静态字段/方法直接引用(如
sun.misc.Unsafe.getUnsafe()):用--add-exports
java --add-exports java.base/sun.misc=ALL-UNNAMED -jar app.jar - Windows 下 jpackage 打包应用:将参数写入
runtime\bin\jvm.options(UTF-8 无 BOM);Linux/macOS 放runtime/image/bin/jvm.options
构建与运行环境需分别配置
不同阶段 JVM 进程独立,参数不能复用:
-
Maven 编译/测试(
mvn compile、mvn test):在maven-surefire-plugin或maven-compiler-plugin中配置<jvmArguments>或<argLine> -
Spring Boot 运行(
mvn spring-boot:run):在spring-boot-maven-plugin的<jvmArguments>中声明 -
IDE 启动(IntelliJ/IDEA):在
Run Configuration → VM Options中填写,注意区分Run和Debug配置
验证是否真正生效
仅看启动不报错不够,需确认功能完整:
- 启动时加
-XX:+PrintModuleResolution,检查目标包是否被成功打开(输出含open java.base/sun.nio.ch to ALL-UNNAMED) - 运行关键路径:如涉及 DirectBuffer 的零拷贝传输、Unsafe 的原子更新、网络工具调用,必须执行端到端业务逻辑验证
- 禁用参数后复现错误,再启用对比,排除缓存或旧版本残留干扰

















