本条基于 Java 17(Minecraft 1.20.1 所用版本)。
Unsafe(sun.misc.Unsafe,位于 jdk.unsupported 模块)提供直接内存读写、字段偏移、CAS、不经构造器创建对象等底层操作,绕过 Java 的访问检查和类型安全。JDK 内部另有一个功能更全的 jdk.internal.misc.Unsafe,默认不对外导出。
获取
构造器私有,通常用 Reflection 读取静态字段 theUnsafe:Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe u = (Unsafe) f.get(null);
由于 jdk.unsupported 开放了 sun.misc 包,这个写法在 Java 17 中依然可用。
常用能力
objectFieldOffset/staticFieldBase/staticFieldOffset配合getObject、putObject、getInt、putInt等:直接读写任意字段,包括final字段。allocateInstance:不调用构造器创建对象。compareAndSwap*/getAndSet*:原子操作。allocateMemory/freeMemory/getAddress/putAddress:堆外内存。- 读写对象头:klassptr 就是用
getInt/putInt改写对象头里的类型指针,从而改变对象在 JVM 眼中的类型。 - 读取
MethodHandles.Lookup.IMPL_LOOKUP等 JDK 内部静态字段,见 MethodHandle。 - 修改 Klass 内部标志:例如清除隐藏类的 hidden 标志,使它被当作普通类,从而能被 retransformClasses / redefineClasses 处理;依赖 JDK 内部布局,不同版本需要适配。
局限
- 偏移量或地址写错会直接导致 JVM 崩溃,而不是抛异常。
- 对 record 和隐藏类的字段,
objectFieldOffset会抛异常。 defineClass已在 JDK 11 移除,defineAnonymousClass已在 JDK 17 移除,需改用MethodHandles.Lookup的对应方法。- JDK 23 起,
sun.misc.Unsafe的内存访问方法被标记为弃用待删除(JEP 471)。