商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > Java中 怎么通过 Unsafe 动态读写对象的 private 内存字段

Java中 怎么通过 Unsafe 动态读写对象的 private 内存字段

  发布于2026-07-15 阅读(0)

扫一扫,手机访问

Ja va 中通过 Unsafe 动态读写对象的 private 字段,这事儿说白了就是绕过 JVM 的访问控制,直接对着对象在堆内存里的偏移地址下黑手。属于底层骚操作,只在极少数场景——比如高性能框架、序列化库、JVM 工具开发中才会用到。**业务代码千万别碰**,而且从 JDK 9 开始已经被严格限制,到了 JDK 17+ 默认就不可用了(得额外加 JVM 参数并声明模块权限才能玩)。

Ja va中通过Unsafe动态读写private字段本质是绕过JVM访问控制、直接操作内存偏移,仅限高性能框架等特定场景,不推荐业务使用;JDK9+强限制,需模块权限或JVM参数;推荐改用VarHandle等官方替代方案。

Ja va中 怎么通过 Unsafe 动态读写对象的 private 内存字段

获取 Unsafe 实例(JDK 9+ 需特殊处理)

UnsafegetUnsafe() 方法已经不对外公开了——只有 Bootstrap ClassLoader 加载的类才能直接调。常见的兼容手法有这么几种:

  • 暴力反射拿单例(JDK 8 还能凑合,JDK 9+ 得加 --illegal-access=permit--add-opens
  • jdk.internal.misc.Unsafe 并配合模块导出(启动参数:--add-modules jdk.unsupported --add-opens ja va.base/jdk.internal.misc=ALL-UNNAMED
  • 更稳妥的做法:老老实实用官方替代方案,比如 VarHandle(JDK 9 引入,线程安全、跨平台、JVM 保护妥妥的)

计算字段内存偏移量

Unsafe 不认字段名,只认内存地址偏移。得靠 objectFieldOffset() 拿到目标 private 字段在对象内的字节偏移:

  • 字段必须是实例字段或静态字段;但 static final 常量会被 JVM 内联,根本没法可靠定位
  • 静态字段用 staticFieldOffset(),实例字段用 objectFieldOffset(Field)
  • 注意:字段偏移量依赖 JVM 实现(比如是否开启指针压缩),不同 JVM 参数下结果可能不一样

执行读写操作(以 int 类型 private 字段为例)

拿到 Unsafe 实例和字段偏移后,就能用原子或非原子方法直接读写了:

  • 读取:unsafe.getInt(obj, offset)(obj 不能是 null,offset 是字段在该对象中的偏移)
  • 写入:unsafe.putInt(obj, offset, newValue)
  • 如果是 private static 字段,用 getInt(null, offset) / putInt(null, offset, v)
  • 其他类型对应 getLonggetObjectputObject 等;如果要保证 volatile 语义,用 getAndSetInt 这类原子方法

关键风险与替代建议

直接操作内存字段,稍不留神就会翻车:

  • JVM 优化(字段重排序、常量折叠、逃逸分析)可能让偏移失效或读到异常值
  • GC 移动对象时,Unsafe 操作还按原地址读写,数据直接错乱(不过对堆内对象,JVM 会保证偏移有效,实际不用手动处理)
  • 模块系统隔离后,反射 + Unsafe 的组合在生产环境大概率直接报错
  • 优先用 VarHandle(支持 private 字段、编译器/JVM 优化友好、有明确的内存模型语义),或者 MethodHandles.Lookup(JDK 15+ 支持私有字段查找)
本文转载于:https://www.php.cn/faq/2823560.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注