最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Java中 怎么通过 Unsafe 动态读写对象的 private 内存字段
时间:2026-07-27 07:59:47 编辑:袖梨 来源:一聚教程网
Java中通过Unsafe动态读写private字段本质是绕过JVM访问控制、直接操作内存偏移,仅限高性能框架等特定场景,不推荐业务使用;JDK9+强限制,需模块权限或JVM参数;推荐改用VarHandle等官方替代方案。
Java 中通过 Unsafe 动态读写对象的 private 字段,本质是绕过 JVM 的访问控制机制,直接操作对象在堆内存中的偏移地址。这属于底层黑盒操作,仅限于特定场景(如高性能框架、序列化库、JVM 工具开发),**不推荐在业务代码中使用**,且自 JDK 9 起被强限制,JDK 17+ 默认不可用(需额外 JVM 参数并声明模块权限)。
获取 Unsafe 实例(JDK 9+ 需特殊处理)
Unsafe 不再提供公开的 getUnsafe() 方法供普通类调用(仅 BootClassLoader 加载的类可获取)。常见兼容方式:
- 通过反射暴力获取单例(JDK 8 可行,JDK 9+ 需 --illegal-access=permit 或 --add-opens)
- 使用
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)获取实例字段偏移 - 注意:字段偏移依赖 JVM 实现(如是否开启指针压缩),不同 JVM 参数下可能变化
执行读写操作(以 int 类型 private 字段为例)
拿到 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等原子方法
关键风险与替代建议
直接操作内存字段极易引发不可预测行为:
- JVM 优化(如字段重排序、常量折叠、逃逸分析)可能导致偏移失效或值异常
- GC 移动对象时,
Unsafe操作仍按原地址读写,造成数据错乱(但对堆内对象,JVM 保证偏移有效,实际无需手动处理) - 模块系统隔离后,反射 + Unsafe 组合在生产环境大概率失败
- 优先使用
VarHandle(支持 private 字段、编译器/JVM 优化友好、有明确内存模型语义)或MethodHandles.Lookup(JDK 15+ 支持私有字段查找)