Unsafe 类核心功能笔记

一、Unsafe 类概述

  • 定位:Java 底层 “后门级” 工具类,绕开 JVM 安全限制,直接操作内存、线程等底层资源。
  • 风险:操作不当会导致 JVM 崩溃、内存泄漏,官方仅允许 JDK 内部类库使用,普通开发需谨慎。
  • 获取方式:通过反射破解访问限制(因 Unsafe.getUnsafe() 有安全校验,普通类调用会抛 SecurityException)。

二、6 大核心功能与示例

1. 内存管理

  • 能力:像 C 指针一样直接分配、释放、操作内存,支持堆外内存(不受 JVM 垃圾回收管理)。
  • 核心方法allocateMemory(分配内存)、freeMemory(释放内存)、copyMemory(内存拷贝)、putInt/getInt(读写内存值)。
  • 示例
1
2
3
4
5
Unsafe unsafe = getUnsafe();
long addr = unsafe.allocateMemory(1024); // 分配 1024 字节内存
unsafe.putInt(addr, 666); // 写 int 值到内存地址
int value = unsafe.getInt(addr); // 从内存地址读 int 值
unsafe.freeMemory(addr); // 释放内存,避免泄漏

2. 非常规对象实例化

  • 能力:直接创建对象实例,且不执行构造方法和初始化逻辑(可绕过私有构造、final 限制)。
  • 核心方法allocateInstance
  • 示例
1
2
3
4
5
6
7
class User {
private User() { // 私有构造,普通方式无法创建
System.out.println("构造方法执行了");
}
}
Unsafe unsafe = getUnsafe();
User user = (User) unsafe.allocateInstance(User.class); // 不执行构造方法

3. 操作对象的属性

  • 能力:直接修改对象的私有属性(包括 final 字段),打破 Java 封装性。
  • 核心方法objectFieldOffset(获取字段内存偏移量)、putObject/getObject(读写字段值)。
  • 示例
1
2
3
4
5
6
7
class Person {
private String name = "初始值";
}
Unsafe unsafe = getUnsafe();
Person p = new Person();
long offset = unsafe.objectFieldOffset(Person.class.getDeclaredField("name"));
unsafe.putObject(p, offset, "修改后的值"); // 直接修改私有字段

4. 操作数据元素

  • 能力:直接操作数组元素的内存地址,实现 “超大数组” 或优化数组访问性能。
  • 核心方法arrayBaseOffset(数组第一个元素偏移量)、arrayIndexScale(数组元素内存增量)。
  • 示例
1
2
3
4
5
int[] arr = new int[3];
Unsafe unsafe = getUnsafe();
long baseOffset = unsafe.arrayBaseOffset(int[].class); // 数组首元素偏移量
long indexScale = unsafe.arrayIndexScale(int[].class); // 元素内存增量(int 为 4 字节)
unsafe.putInt(arr, baseOffset + indexScale * 1, 999); // 给 arr[1] 赋值 999

5. 多线程支持

  • 能力:是 Java 并发包(java.util.concurrent)的底层支撑,提供 CAS 操作、线程挂起 / 唤醒等能力。
    • CAS 操作compareAndSwapInt/compareAndSwapObject(原子比较并交换,AtomicInteger 底层实现)。
      示例(模拟 AtomicInteger 自增):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyAtomicInteger {
private volatile int value;
private static long offset;
private static Unsafe unsafe;
static {
unsafe = getUnsafe();
offset = unsafe.objectFieldOffset(MyAtomicInteger.class.getDeclaredField("value"));
}
public int incrementAndGet() {
int current;
do {
current = unsafe.getIntVolatile(this, offset);
} while (!unsafe.compareAndSwapInt(this, offset, current, current + 1));
return current + 1;
}
}

线程挂起 / 唤醒park(挂起线程)、unpark(唤醒线程)(LockSupport 底层实现)。
示例:

1
2
3
4
5
6
Thread t = new Thread(() -> {
unsafe.park(); // 挂起线程
System.out.println("线程被唤醒");
});
t.start();
unsafe.unpark(t); // 唤醒线程

6. 内存屏障

  • 能力:阻止指令重排序,保证多线程下的内存可见性(类似 volatile 关键字底层实现)。
  • 核心方法loadFence(读屏障)、storeFence(写屏障)、fullFence(全屏障)。
  • 示例(保证读可见性):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Data { private int value; }
Data data = new Data();
Unsafe unsafe = getUnsafe();

// 线程 1 写数据
new Thread(() -> {
data.value = 123;
unsafe.storeFence(); // 写屏障:保证写操作对其他线程可见
}).start();

// 线程 2 读数据
new Thread(() -> {
unsafe.loadFence(); // 读屏障:保证从主内存读最新值
System.out.println(data.value); // 输出 123
}).start();

三、使用建议

  • 适用场景:仅用于 JDK 内部类库、高性能框架(如 Netty、Hadoop)的底层开发。
  • 替代方案:普通开发优先使用 Java 标准库的安全封装(如 AtomicIntegerLockSupportvolatile)。
  • 兼容性sun.misc.Unsafe 是非标准 API,JDK 9+ 可能迁移到 jdk.internal.misc.Unsafe,需注意版本差异。

附:Unsafe 实例获取工具方法

1
2
3
4
5
6
7
8
9
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);
}
}