Java中通过Unsafe动态读写private字段本质是绕过JVM访问控制、直接操作内存偏移,仅限高性能框架等特定场景,不推荐业务使用;JDK9+强限制,需模块权限或JVM参数;推荐改用VarHandle等官方替代方案。
Java 中通过 Unsafe 动态读写对象的 private 字段,本质是绕过 JVM 的访问控制机制,直接操作对象在堆内存中的偏移地址。这属于底层黑盒操作,仅限于特定场景(如高性能框架、序列化库、JVM 工具开发),**不推荐在业务代码中使用**,且自 JDK 9 起被强限制,JDK 17+ 默认不可用(需额外 JVM 参数并声明模块权限)。
Unsafe 不再提供公开的 getUnsafe() 方法供普通类调用(仅 BootClassLoader 加载的类可获取)。常见兼容方式:
jdk.internal.misc.Unsafe 并通过模块导出(需 JVM 启动参数:--add-modules jdk.unsupported --add-opens java.base/jdk.internal.misc=ALL-UNNAMED)VarHandle,JDK 9 引入,线程安全、跨平台、受 JVM 保护)Unsafe 不识别字段名,只认内存地址偏移。必须通过 objectFieldOffset() 获取目标 private 字段在对象内的字节偏移:
static final 常量会被内联,无法可靠定位staticFieldOffset() 获取静态字段偏移;objectFieldOffset(Field) 获取实例字段偏移拿到 Unsafe 实例和字段偏移后,即可用原子/非原子方法读写:
立即学习“Java免费学习笔记(深入)”;
unsafe.getInt(obj, offset)(obj 非 null,offset 是该字段在 obj 中的偏移)unsafe.putInt(obj, offset, newValue)
private static 字段,用 getInt(null, offset) / putInt(null, offset, v)
getLong、getObject、putObject 等;volatile 语义用 getAndSetInt 等原子方法直接操作内存字段极易引发不可预测行为:
Unsafe 操作仍按原地址读写,造成数据错乱(但对堆内对象,JVM 保证偏移有效,实际无需手动处理)VarHandle(支持 private 字段、编译器/JVM 优化友好、有明确内存模型语义)或 MethodHandles.Lookup(JDK 15+ 支持私有字段查找)