JEP 草案:为移除而弃用 sun.misc.unsafe 中的内存访问方法
Java 计划将低层的 `sun.misc.Unsafe` 内存访问方法弃用,并转向更安全的标准 API,如 `VarHandle` 和 `MemorySegment`,这再次点燃了 JVM 上性能与安全之间长期存在的紧张关系。许多人欢迎更强的保证和更清晰的“危险行为”边界,但也有人警告,强制边界检查以及失去“零开销”访问,可能会伤害压缩、加密、数据库和交易系统等高性能负载,进而把它们推向原生代码或其他语言。该提案的成败取决于新 API 和可选标志能否在清理平台对未记录内部实现依赖的同时,保留接近 `Unsafe` 的性能。
JEP 的背景
- JEP 提议弃用并最终移除
sun.misc.Unsafe的内存访问方法,改为使用:- 用于堆内存的
VarHandle。 - 用于堆外内存的
MemorySegment(Foreign Function & Memory API)。
- 用于堆内存的
- 这些方案被定位为更安全、性能更好、更稳定,并且与工具链集成更佳。
- 弃用意味着会有较长的过渡期;移除则只会在多个 LTS 版本之后进行。
性能与边界检查
- 争议核心:
VarHandle/MemorySegment总是进行边界检查,而Unsafe可以避免这些检查。 - 一些发帖者认为边界检查可能带来 10% 以上的成本,在紧密循环中有时甚至更高,尤其是在以下场景中:
- 压缩/加密。
- 解析密集型负载(例如 One Billion Row Challenge)。
- 低层数据结构和类似 HFT 的系统。
- 也有人反驳说:
- 现代 JIT 往往可以消除边界检查。
Unsafe可能会抑制其他优化,有时表现反而更差。
- JIT 为
MemorySegment消除检查的能力受到限制,而且需要不太直观的模式;有些人认为这很脆弱。
安全性、未定义行为与完整性
- 支持移除的论点:
Unsafe会引入未定义行为、崩溃和安全问题。- 平台希望具备完整性:运行时和应用都应知道哪里可能破坏不变量。
- 这也符合行业从无检查的未定义行为转向更安全实践的趋势。
- 批评者认为:
- 高级用户是有意用安全性换取速度。
- 把这件事当作意识形态问题,会忽视现实中的性能需求。
替代方案与变通办法
- 内部
jdk.internalAPI 仍可通过命令行标志(--add-exports)访问,被视为一种“中间路线”。 - 有人建议使用原生 FFI(JNI/Panama)作为性能上的逃生通道;也有人指出这会带来调用开销和复杂性。
- 还有人提议提供明确的“无边界检查”模式,或受限的无边界
MemorySegment,而不是全面弃用。
生态系统与迁移担忧
- 人们担心会影响依赖
Unsafe的大型 Java 数据系统(Kafka、Cassandra、Elasticsearch、HBase)。 - 担忧如果无法提供足够的等价替代,会:
- 促使用户继续停留在旧版 JDK(类似 8/11 和模块化带来的情况)。
- 把性能敏感型用户推向 C++、Rust、Go 或 C#。
- 也有人认为,大多数库都可以几乎无影响地迁移,真正需要
Unsafe级语义的代码只占很小一部分。