sun.misc.Unsafe 是 JDK 內部類,提供底層內存操作、CAS 原子操作、類動態加載、線程調度、數組越界訪問等繞過 JVM 安全檢查的能力。它是 Java 並發包、Netty、HBase、Kafka 等高性能框架的基石。
public static Unsafe getUnsafe() {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
IDE 會報「restricted API」錯誤,運行無礙。可在 Compiler 設置中將「Deprecated and restricted API」改為 Warning。
public class Player {
private int age = 12;
private Player() { this.age = 50; } // 私有構造器
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
// 測試
Player p = (Player) unsafe.allocateInstance(Player.class);
System.out.println(p.getAge()); // 0(構造器未執行,字段默認值)
p.setAge(45);
System.out.println(p.getAge()); // 45
應用:反序列化框架(Kryo、Hessian)、單例模式測試繞過。
public class SuperArray {
private final long address;
private final long size;
private static final int BYTE = 1;
public SuperArray(long size) {
this.size = size;
this.address = unsafe.allocateMemory(size * BYTE); // 直接內存分配
}
public void set(long i, byte value) {
unsafe.putByte(address + i * BYTE, value);
}
public byte get(long i) {
return unsafe.getByte(address + i * BYTE);
}
public void free() {
unsafe.freeMemory(address); // 必須手動釋放,否則內存洩漏
}
}
// 測試:創建 2 * Integer.MAX_VALUE 大小數組
long SUPER_SIZE = (long) Integer.MAX_VALUE * 2;
SuperArray arr = new SuperArray(SUPER_SIZE);
arr.set(Integer.MAX_VALUE + 100, (byte) 3);
System.out.println(arr.get(Integer.MAX_VALUE + 100)); // 3
arr.free();
風險:OOM 機率大,JVM 崩潰無堆疊,生產環境極度謹慎。
String password = "l00k@myHor$e";
String fake = password.replaceAll(".", "?"); // "????????????"
// 將 fake 的字節覆蓋到 password 所在內存
unsafe.copyMemory(fake, 0L, null, unsafe.objectFieldOffset(String.class.getDeclaredField("value")), password.length());
System.out.println(password); // "????????????"
原理:字符串在常量池/堆中可被內存掃描工具讀取,覆蓋後 GC 前不可讀。
byte[] classBytes = readClassFile("/tmp/MyClass.class");
Class<?> clazz = unsafe.defineClass(null, classBytes, 0, classBytes.length, null, null);
Object instance = clazz.getDeclaredConstructor().newInstance();
clazz.getMethod("run").invoke(instance);
應用:JVM 熱部署、代碼生成框架(ASM、ByteBuddy 底層)、動態代理增強。
// AtomicInteger 核心源碼片段
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
所有 java.util.concurrent.atomic.*、LongAdder、ConcurrentHashMap 計數器底層均依賴此能力。
| 類別 | 代表方法 | 用途 |
|---|---|---|
| 內存分配/釋放 | allocateMemory, freeMemory, reallocateMemory | 直接內存管理 |
| 內存讀寫 | getByte/putByte, getInt/putInt, getLong/putLong, getObject/putObject | 堆外數據結構 |
| CAS 原子操作 | compareAndSwapInt/Long/Object | 無鎖並發 |
| 類/對象操作 | allocateInstance, defineClass, objectFieldOffset, staticFieldOffset | 反射增強、序列化 |
| 線程調度 | park, unpark, monitorEnter/Exit | AQS 底層支撐 |
| 數組操作 | arrayBaseOffset, arrayIndexScale, getAndAddInt | 高性能數組處理 |
sun.misc.Unsafe 仍可用但需 --add-exports,JDK 17+ 建議遷移至 jdk.internal.misc.Unsafe 或 VarHandleallocateMemory 必須配對 freeMemory,建議封裝 AutoCloseableVarHandle、MemorySegment (JEP 387) 提供更安全的標準 APIUnsafe 是「核按鈕」,框架作者的利器,業務開發極少直接使用。理解其原理有助於讀懂高性能框架源碼、排查 JVM 底層問題、設計無鎖數據結構。