unsafe.getunsafe() 总抛 securityexception 是因 jvm 硬性禁止普通代码获取实例;唯一可行绕过方式是反射读取私有字段 theunsafe 或加 boot classpath(jdk 9+ 需 --add-opens)。

Unsafe.getUnsafe() 为什么总抛出 SecurityException
因为 JVM 默认禁止普通代码获取 Unsafe 实例,这是硬性安全限制,不是配置没开或路径不对。
真正能绕过的方式只有两种:一是用反射强行读取私有静态字段 theUnsafe(JDK 8–17 都有效),二是通过启动参数 -Xbootclasspath/a: 把你的类加到 boot classpath(极少用,且 JDK 9+ 模块系统后更难)。
常见错误现象:java.lang.SecurityException: Unsafe 不是因为你写错了,而是因为你没走反射那条路。
- 反射获取示例:
Field f = Unsafe.class.getDeclaredField("theUnsafe");<br>f.setAccessible(true);<br>Unsafe u = (Unsafe) f.get(null); - JDK 9+ 模块系统下,还需额外加 JVM 参数:
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED - 别信网上“改 JVM 源码”或“重编译 rt.jar”的说法——不现实,也无必要
直接内存操作:putLong、getInt 等方法的地址偏移怎么算
这些方法不认 Java 对象字段名,只认从对象起始地址开始的字节偏移量。偏移量不是你猜的,必须用 objectFieldOffset() 或 staticFieldOffset() 算出来。
立即学习“Java免费学习笔记(深入)”;
比如对一个 volatile long value 字段调用 putLong(obj, offset, 123),如果 offset 错了,要么写到别的字段头上,要么触发 SIGSEGV(尤其在非 HotSpot VM 上)。
- 实例字段偏移:
unsafe.objectFieldOffset(Foo.class.getDeclaredField("value")) - 静态字段偏移:
unsafe.staticFieldOffset(Foo.class.getDeclaredField("STATIC_VALUE")) - 注意:字段必须是 public 或 setAccessible(true),否则抛
NoSuchFieldException - 偏移量在同一个 JVM 进程内是稳定的,但不同 JVM 版本/平台(如 x86 vs aarch64)可能不同
CAS 操作:compareAndSwapInt 和 compareAndSwapObject 的原子性边界在哪
compareAndSwapInt 和 compareAndSwapObject 是硬件级原子指令封装,但它们只保证单个字段的读-比较-写三步不可中断,不提供内存屏障语义(除非你显式搭配 loadFence()/storeFence())。
典型误用:以为 CAS 成功后,其他线程立刻能看到该字段关联的副作用(比如更新了某个缓存数组),其实没有。JVM 可能重排序,CPU 缓存也可能未同步。
- Java 8+ 中,
VarHandle是更安全的替代方案,它自动注入正确屏障 - 若坚持用
Unsafe,CAS 后需手动加unsafe.fullFence()(或unsafe.storeFence()+unsafe.loadFence())才能模拟 volatile 语义 -
compareAndSwapObject对 null 值比较要小心:null在底层是 0 地址,某些 JVM(如 ZGC)会拦截 0 地址访问并抛NullPointerException
Unsafe.allocateMemory 申请的内存谁来 free
没人帮你 free。这不是堆内存,不归 GC 管,allocateMemory 返回的是裸指针(long 类型地址),必须配对调用 freeMemory,漏掉就真泄漏了。
而且不能用 freeMemory 去释放 DirectByteBuffer 底层的内存——那是 Cleaner 管的,类型不兼容,强转或乱调会 crash。
- 申请后立即记录地址:
long addr = unsafe.allocateMemory(1024); - 用完必须显式释放:
unsafe.freeMemory(addr); - 别依赖 try-with-resources:
Unsafe不实现AutoCloseable - JDK 17+ 中,
MemorySegment(来自 Panama 项目)才是推荐的替代方案,生命周期可托管
Unsafe 的危险不在函数名吓人,而在它把原本由 JVM 隐藏的细节全摊开给你——偏移算错、屏障漏加、内存忘收,每个点都刚好卡在“运行时不出错,压测才崩”的位置。










