首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

深度解析 sun.misc.Unsafe:Java 魔法類的核心能力與實戰

     編輯:Java 相關知識

什麼是 Unsafe

sun.misc.Unsafe 是 JDK 內部類,提供底層內存操作、CAS 原子操作、類動態加載、線程調度、數組越界訪問等繞過 JVM 安全檢查的能力。它是 Java 並發包、Netty、HBase、Kafka 等高性能框架的基石。

獲取 Unsafe 實例

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。

五大核心能力實戰

1. 繞過構造器創建對象(單例破壞者)

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)、單例模式測試繞過。

2. 直接內存操作實現超大數組(突破 Integer.MAX_VALUE)

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 崩潰無堆疊,生產環境極度謹慎。

3. 內存加密:覆蓋敏感字符串(密碼防掃描)

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 前不可讀。

4. 運行時動態定義類(熱加載基石)

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 底層)、動態代理增強。

5. CAS 原子操作(並發包核心)

// 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 計數器底層均依賴此能力。

Unsafe 方法分類速查

類別代表方法用途
內存分配/釋放allocateMemory, freeMemory, reallocateMemory直接內存管理
內存讀寫getByte/putByte, getInt/putInt, getLong/putLong, getObject/putObject堆外數據結構
CAS 原子操作compareAndSwapInt/Long/Object無鎖並發
類/對象操作allocateInstance, defineClass, objectFieldOffset, staticFieldOffset反射增強、序列化
線程調度park, unpark, monitorEnter/ExitAQS 底層支撐
數組操作arrayBaseOffset, arrayIndexScale, getAndAddInt高性能數組處理

避坑指南

  1. 版本兼容性:JDK 9+ 模組化後 sun.misc.Unsafe 仍可用但需 --add-exports,JDK 17+ 建議遷移至 jdk.internal.misc.Unsafe 或 VarHandle
  2. 內存洩漏:allocateMemory 必須配對 freeMemory,建議封裝 AutoCloseable
  3. 安全性:錯誤的偏移量導致 JVM Crash,生產代碼需大量測試驗證
  4. 替代方案:Java 9+ VarHandle、MemorySegment (JEP 387) 提供更安全的標準 API

Unsafe 是「核按鈕」,框架作者的利器,業務開發極少直接使用。理解其原理有助於讀懂高性能框架源碼、排查 JVM 底層問題、設計無鎖數據結構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved