Java Unsafe类
Java Unsafe类(JDK 17中位于jdk.internal.misc)是JDK内部提供的底层工具类,需通过反射获取实例,提供直接操作内存(堆外内存分配/释放、字节级操作)、对象字段(绕过访问控制)、CAS原子操作及线程挂起(park/unpark)等核心能力,常用于高性能框架(如Netty的零拷贝缓冲区)、无锁并发数据结构(如AtomicInteger底层实现)和直接内存处理场景;但因其非公开API特性、内存管理风险(易导致泄漏或崩溃)、线程安全问题及JDK版本兼容性隐患,必须严格限制使用:仅在性能瓶颈不可通过JDK公开API解决、团队深度理解风险且经过严格测试的场景下使用,绝对避免在普通生产代码中直接应用,否则将严重危及程序稳定性。
一、Unsafe类概述
Unsafe类是Java中一个极其底层的类,位于jdk.internal.misc.Unsafe包中(JDK 9+后被封装到jdk.unsupported模块),而非之前的sun.misc包。它提供了直接操作内存、对象、线程等核心资源的能力,绕过Java语言的安全检查和语法限制。
1.1 为什么叫"Unsafe"?
这个名称直接反映了它的本质:它提供了突破Java安全沙箱的能力,允许开发者直接与JVM底层交互,包括内存、CPU指令、线程调度等。"Unsafe这个词直译过来就是'不安全的',从名字里我们也大概能看来Java的开发者们对它有些不放心。"
1.2 Unsafe类在JDK 17中的变化
在JDK 17中,Unsafe类已经完全移出sun.misc包,位于jdk.internal.misc.Unsafe,这意味着:
- 不能直接导入:import sun.misc.Unsafe; 将会失败
- 必须通过反射获取:即使JDK内部类也无法直接访问,需要通过反射获取实例
- 非公开API:它是JDK内部使用的API,不保证向后兼容性
二、Unsafe类的核心功能
Unsafe类提供了多种低级操作能力,主要分为以下几类:
2.1 对象操作
| 方法 | 说明 |
|---|---|
| allocateInstance(Class<?> cls) | 创建未初始化的对象实例(绕过构造方法) |
| getObject(Object o, long offset) | 获取对象字段的值(绕过访问限制) |
| putObject(Object o, long offset, Object x) | 设置对象字段的值(绕过访问限制) |
| getObjectVolatile(Object o, long offset) | 获取对象字段的值(带有volatile语义) |
| putObjectVolatile(Object o, long offset, Object x) | 设置对象字段的值(带有volatile语义) |
2.2 内存操作
| 方法 | 说明 |
|---|---|
| allocateMemory(long size) | 在堆外内存分配空间 |
| freeMemory(long address) | 释放堆外内存 |
| setMemory(long address, long size, byte value) | 设置内存区域的值 |
| copyMemory(long srcAddress, long destAddress, long size) | 复制内存区域 |
| getByte(long address) | 从指定地址获取字节 |
| putByte(long address, byte value) | 向指定地址写入字节 |
2.3 数组操作
| 方法 | 说明 |
|---|---|
| arrayBaseOffset(Class<?> arrayClass) | 获取数组基础偏移量 |
| arrayIndexScale(Class<?> arrayClass) | 获取数组索引缩放比例 |
| getLongArrayElement(Object array, int index) | 获取数组中long类型的元素 |
| putLongArrayElement(Object array, int index, long value) | 设置数组中long类型的元素 |
2.4 线程操作
| 方法 | 说明 |
|---|---|
| park(boolean isAbsolute, long time) | 挂起当前线程 |
| unpark(Thread thread) | 恢复指定线程 |
| currentThread() | 获取当前线程 |
2.5 CAS操作
| 方法 | 说明 |
|---|---|
| compareAndSwapObject(Object o, long offset, Object expected, Object update) | 对象字段的CAS操作 |
| compareAndSwapInt(Object o, long offset, int expected, int update) | int字段的CAS操作 |
| compareAndSwapLong(Object o, long offset, long expected, long update) | long字段的CAS操作 |
2.6 其他功能
| 方法 | 说明 |
|---|---|
| staticFieldOffset(Field f) | 获取静态字段的偏移量 |
| objectFieldOffset(Field f) | 获取对象字段的偏移量 |
| staticFieldBase(Field f) | 获取静态字段所属的类 |
| addressSize() | 获取地址大小 |
| pageSize() | 获取页面大小 |
三、获取Unsafe实例(JDK 17)
在JDK 17中,获取Unsafe实例的方式与之前相同,但需要通过反射,因为Unsafe类被封装到jdk.internal.misc包中:
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class UnsafeUtils {
private static final Unsafe unsafe;
static {
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
unsafe = (Unsafe) theUnsafeField.get(null);
} catch (Exception e) {
throw new RuntimeException("无法获取Unsafe实例", e);
}
}
public static Unsafe getUnsafe() {
return unsafe;
}
}
四、Unsafe类的适用场景
4.1 高性能框架(如Netty、Akka)
Unsafe类在高性能网络库中广泛使用,用于直接操作内存,提高网络通信性能。例如,Netty使用Unsafe来实现零拷贝的字节缓冲区。
4.2 并发编程(无锁数据结构)
Unsafe类提供了CAS操作,是实现无锁数据结构的基础。JUC包(java.util.concurrent)中的许多类都依赖Unsafe实现。
代码示例:基于Unsafe的原子计数器
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class AtomicCounter {
private volatile long count = 0;
private static final Unsafe unsafe;
private static final long countOffset;
static {
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
unsafe = (Unsafe) theUnsafeField.get(null);
countOffset = unsafe.objectFieldOffset(AtomicCounter.class.getDeclaredField("count"));
} catch (Exception e) {
throw new RuntimeException("初始化失败", e);
}
}
public void increment() {
long current;
do {
current = unsafe.getLongVolatile(this, countOffset);
} while (!unsafe.compareAndSwapLong(this, countOffset, current, current + 1));
}
public long getCount() {
return unsafe.getLongVolatile(this, countOffset);
}
public static void main(String[] args) throws InterruptedException {
AtomicCounter counter = new AtomicCounter();
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.increment();
}
});
threads[i].start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println("最终计数值: " + counter.getCount());
}
}
4.3 线程挂起与恢复(park/unpark)
Unsafe类提供了park和unpark方法,可以实现比wait/notify更灵活的线程同步机制。
代码示例:基于Unsafe的阻塞队列
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class UnsafeBlockingQueue<E> {
private static final Unsafe unsafe;
private static final long headOffset;
private static final long tailOffset;
private static final long countOffset;
private static final int arrayIndexScale;
private final Object[] items;
private volatile int head = 0;
private volatile int tail = 0;
private volatile int count = 0;
private final int capacity;
static {
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
unsafe = (Unsafe) theUnsafeField.get(null);
headOffset = unsafe.objectFieldOffset(UnsafeBlockingQueue.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset(UnsafeBlockingQueue.class.getDeclaredField("tail"));
countOffset = unsafe.objectFieldOffset(UnsafeBlockingQueue.class.getDeclaredField("count"));
arrayIndexScale = unsafe.arrayIndexScale(Object[].class);
} catch (Exception e) {
throw new RuntimeException("初始化Unsafe失败", e);
}
}
public UnsafeBlockingQueue(int capacity) {
this.capacity = capacity;
this.items = new Object[capacity];
}
public void put(E e) throws InterruptedException {
while (true) {
int currentCount = unsafe.getIntVolatile(this, countOffset);
if (currentCount < capacity) {
// 尝试添加元素
int index = tail;
unsafe.putObjectVolatile(this, tailOffset, (index + 1) % capacity);
unsafe.putObject(items, (long) index * arrayIndexScale, e);
unsafe.putIntVolatile(this, countOffset, currentCount + 1);
return;
}
// 队列满,挂起当前线程
unsafe.park(false, 0);
}
}
public E take() throws InterruptedException {
while (true) {
int currentCount = unsafe.getIntVolatile(this, countOffset);
if (currentCount > 0) {
// 尝试获取元素
int index = head;
E e = (E) unsafe.getObject(items, (long) index * arrayIndexScale);
unsafe.putObjectVolatile(this, headOffset, (index + 1) % capacity);
unsafe.putIntVolatile(this, countOffset, currentCount - 1);
return e;
}
// 队列空,挂起当前线程
unsafe.park(false, 0);
}
}
public static void main(String[] args) throws InterruptedException {
UnsafeBlockingQueue<String> queue = new UnsafeBlockingQueue<>(10);
Thread producer = new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
queue.put("Item " + i);
System.out.println("Produced: " + i);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
Thread consumer = new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
String item = queue.take();
System.out.println("Consumed: " + item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
producer.start();
consumer.start();
producer.join();
consumer.join();
}
}
4.4 直接内存操作(堆外内存)
Unsafe类允许直接操作堆外内存,这在处理大数据量时非常有用,避免了Java堆内存的GC压力。
代码示例:直接内存操作
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class DirectMemoryExample {
private static final Unsafe unsafe;
static {
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
unsafe = (Unsafe) theUnsafeField.get(null);
} catch (Exception e) {
throw new RuntimeException("获取Unsafe失败", e);
}
}
public static void main(String[] args) {
// 分配1024字节的堆外内存
long address = unsafe.allocateMemory(1024);
try {
// 写入数据
unsafe.putByte(address, (byte) 65); // 'A'
unsafe.putByte(address + 1, (byte) 66); // 'B'
unsafe.putByte(address + 2, (byte) 67); // 'C'
// 读取数据
byte b1 = unsafe.getByte(address);
byte b2 = unsafe.getByte(address + 1);
byte b3 = unsafe.getByte(address + 2);
System.out.println("读取数据: " + (char)b1 + (char)b2 + (char)b3);
// 复制内存
unsafe.copyMemory(address, address + 4, 3);
// 读取复制后的数据
byte b4 = unsafe.getByte(address + 4);
byte b5 = unsafe.getByte(address + 5);
byte b6 = unsafe.getByte(address + 6);
System.out.println("复制后数据: " + (char)b4 + (char)b5 + (char)b6);
} finally {
// 释放内存
unsafe.freeMemory(address);
}
}
}
4.5 对象字段操作(绕过访问限制)
Unsafe类可以绕过Java的访问控制,直接操作对象的私有字段,这在某些需要深度定制的场景中很有用。
代码示例:绕过私有字段限制
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class PrivateFieldExample {
private static final Unsafe unsafe;
private static final long valueOffset;
static {
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
unsafe = (Unsafe) theUnsafeField.get(null);
Field valueField = PrivateFieldExample.class.getDeclaredField("value");
valueOffset = unsafe.objectFieldOffset(valueField);
} catch (Exception e) {
throw new RuntimeException("初始化失败", e);
}
}
private int value = 42;
public void setValue(int newValue) {
unsafe.putInt(this, valueOffset, newValue);
}
public int getValue() {
return unsafe.getInt(this, valueOffset);
}
public static void main(String[] args) {
PrivateFieldExample example = new PrivateFieldExample();
System.out.println("初始值: " + example.getValue());
example.setValue(100);
System.out.println("修改后值: " + example.getValue());
}
}
五、使用Unsafe类的注意事项
5.1 非公开API
- Unsafe类是JDK内部使用的API,不保证向后兼容性
- 在未来的JDK版本中可能会被移除或修改
5.2 内存管理风险
- Unsafe提供的内存操作不受JVM管理(堆外内存)
- 必须手动管理内存,否则会导致内存泄漏
- 释放内存后,继续使用该内存会导致未定义行为
5.3 线程安全问题
- Unsafe类的方法通常不是线程安全的
- 需要开发者自行保证线程安全
5.4 程序稳定性风险
- 错误使用Unsafe可能导致程序崩溃、内存泄漏
- 严重时甚至可能导致JVM崩溃
5.5 不要用于生产代码
"在大多数情况下,建议使用Java的公开API来实现功能。"只有在确实需要更高性能且了解所有风险的情况下,才考虑使用Unsafe。
六、总结
Unsafe类是一个功能强大的工具,但它是一把双刃剑:
✅ 优点:
- 提供底层操作能力
- 可以实现高性能的无锁数据结构
- 可以直接操作内存,避免GC压力
❌ 缺点:
- 非公开API,可能在未来的JDK版本中被移除
- 使用不当可能导致程序崩溃、内存泄漏
- 需要开发者自行保证线程安全和内存管理
最佳实践建议
- 仅在必要时使用:在性能瓶颈确实存在且无法通过其他方式解决时才考虑使用
- 使用封装:创建安全的封装类,避免直接暴露Unsafe方法
- 严格测试:对使用Unsafe的代码进行严格的测试
- 避免在生产环境使用:除非有充分的理由且团队对Unsafe有深入理解
"Unsafe类是一把双刃剑。"它为高性能编程提供了可能性,但也带来了巨大的风险。在JDK 17中,虽然Unsafe类仍然可用,但开发者应谨慎对待,避免在普通应用中直接使用。
重要提示:在JDK 17中,Unsafe类位于jdk.internal.misc.Unsafe,如果尝试使用sun.misc.Unsafe,也可正常使用。同时,由于Java模块系统的限制,即使通过反射获取Unsafe实例,也需要确保在正确的模块路径下运行。
- 感谢你赐予我前进的力量

