如何在JRE9上运行JDK8编译且依赖Unsafe的代码?
首先,你遇到的问题其实和JDK9引入的**模块系统(Project Jigsaw)**密切相关——虽然sun.misc.Unsafe没有被移除,但它的访问规则、编译/运行时可见性发生了根本性变化,这就是你碰到兼容性问题的核心原因。
为什么会抛出NoSuchMethodError?
JDK9之后,sun.misc.Unsafe被归类到jdk.unsupported模块中,这个模块默认是不对外暴露内部包的。当你用JDK8编译的代码在JDK9上运行时,JVM虽然能找到Unsafe类,但模块系统会限制未命名模块(你的应用代码默认属于这类)访问它的方法,最终就出现了「方法明明存在却找不到」的NoSuchMethodError。
至于无法用JDK9重新编译的问题,是因为JDK9默认会严格限制对内部API(比如sun.misc下的类)的访问,编译器直接会报错说找不到sun.misc.Unsafe,或者不允许访问这类非标准API。
如何修复这个问题?
1. 让JDK9运行时允许访问Unsafe
如果只是想让JDK8编译好的代码在JDK9上跑起来,你需要给JVM添加启动参数,放开jdk.unsupported模块对未命名模块的sun.misc包访问:
java --add-exports jdk.unsupported/sun.misc=ALL-UNNAMED 你的主类名
这个参数的意思是:把jdk.unsupported模块下的sun.misc包,导出给所有未命名模块(也就是你的应用代码)。
2. 用JDK9重新编译代码
如果要在JDK9环境下重新编译依赖Unsafe的代码,需要同时给编译器添加参数,允许访问内部API:
javac --add-exports jdk.unsupported/sun.misc=ALL-UNNAMED 你的Java文件.java
编译完成后,运行时同样需要加上上面提到的--add-exports参数才能正常执行。
3. 长远的替代方案(推荐)
sun.misc.Unsafe本身是内部API,Oracle从未承诺过它的向后兼容性,JDK9之后的版本对它的限制只会越来越严格。如果可能的话,建议替换成标准API:
- 内存操作场景:可以用
java.nio.ByteBuffer(底层同样依赖Unsafe,但属于标准API,兼容性有保障) - 对象/字段操作场景:优先使用Java反射API,JDK11及以上版本可以用
java.lang.invoke.VarHandle——这是官方推荐的Unsafe替代方案。
关于Java向后兼容性的误解
Java的向后兼容性承诺只覆盖标准API,内部API(比如sun.*、com.sun.*下的类)不在这个范围内。这类API随时可能被修改、隐藏甚至移除,依赖它们的代码在版本升级时很容易出问题——这也是Oracle一直强调不要使用内部API的原因。
你的测试类的解决方法
你的测试类依赖sun.misc.Unsafe,同样会遇到上述问题。按照下面的步骤操作就能正常运行:
- 编译测试类:
javac --add-exports jdk.unsupported/sun.misc=ALL-UNNAMED UnsafeTest.java
- 运行测试类:
java --add-exports jdk.unsupported/sun.misc=ALL-UNNAMED UnsafeTest
内容的提问来源于stack exchange,提问作者HPCS

