Unsafe.getUnsafe()抛SecurityException因JVM硬编码限制仅Bootstrap类加载器可调用;正确方式是反射获取theUnsafe字段并setAccessible(true),JDK9+需--add-opens参数。

Unsafe.getUnsafe() 为什么总抛出 SecurityException
因为 Unsafe.getUnsafe() 默认只允许 Bootstrap 类加载器加载的类调用,你自己写的代码几乎必然走 AppClassLoader,直接调就炸。这不是权限配置问题,是 JVM 硬编码的校验逻辑。
- 别试图用
-Dsun.misc.Unsafe.allowed=true—— 这个 JVM 参数根本不存在,纯属谣言 - 正确姿势是用反射绕过:先获取
Unsafe的私有静态字段theUnsafe,再通过Field.setAccessible(true)强行读取 - 注意 JDK 9+ 模块系统限制:反射访问
sun.misc.Unsafe会触发IllegalAccessError,得加启动参数--add-opens java.base/sun.misc=ALL-UNNAMED
allocateMemory 和 allocateInstance 的核心区别
allocateMemory 是真·裸分配堆外内存,类似 C 的 malloc;allocateInstance 则是在堆内跳过构造器创建对象,不触发任何 init 方法,但对象仍受 GC 管理。
-
allocateMemory返回的是原始地址(long),后续必须手动配对调用freeMemory,漏掉就是内存泄漏 -
allocateInstance不调用构造函数,所以字段全是默认值(null、0、false),连 final 字段都能绕过初始化 —— 但 JDK 9+ 对 final 字段写入做了额外检查,可能抛RuntimeException - 两者都不经过 JIT 优化路径,JVM 可能无法对其做逃逸分析或栈上分配
putLong / getInt 等方法的地址偏移怎么算
偏移量不是随便传个数字,它必须是目标字段在对象内存布局中的真实字节偏移,而这个值由 JVM 决定,不同版本、不同平台(x86 vs aarch64)、甚至不同 GC 算法都可能导致差异。
- 永远别手算偏移:用
Unsafe.objectFieldOffset(Field)获取,比如unsafe.objectFieldOffset(MyClass.class.getDeclaredField("value")) - 如果字段是 static,得用
staticFieldOffset+staticFieldBase组合,单独用 offset 会读错内存 - 对数组元素操作要用
arrayBaseOffset和arrayIndexScale计算索引,例如base + index * scale,否则在压缩 OOP 开启时必越界
JDK 17+ 还能用 sun.misc.Unsafe 吗
能,但越来越难 —— 它已被标记为 @Deprecated(forRemoval = true),且模块系统默认彻底封死访问。JDK 21 起,Unsafe 的部分关键方法(如 defineAnonymousClass)已移除。
立即学习“Java免费学习笔记(深入)”;
- 替代方案不是没有:JEP 442(Foreign Function & Memory API)是官方正统路径,但目前(JDK 21)仍是 preview 特性,API 不稳定
- 如果你还在用
Unsafe做高性能缓存、对象池或序列化,现在就得评估迁移成本:MemorySegment和VarHandle虽安全,但性能损耗在 5%–20% 区间浮动,取决于访问模式 - 最现实的过渡方式:把
Unsafe封装成 SPI 接口,运行时按 JDK 版本自动 fallback 到VarHandle或MethodHandle
真正麻烦的从来不是“怎么调用”,而是“怎么确保跨 JDK 版本、跨 GC、跨 CPU 架构下行为一致”——这玩意儿没单元测试能覆盖全,只能靠压测和内存快照反复对齐。


















