Unsafe应用

Unsafe位于sun.misc包下,主要提供一些用于执行低级别不安全操作的方法,如直接访问系统内存资源自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。

Unsafe类使Java拥有类似C语言指针一样操作内存空间的能力,增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大。

Unsafe类为单例实现,提供静态方法getUnsafe获取Unsafe实例,仅当调用getUnsafe方法的类为引导类加载器所加载时才合法,否则抛出SecurityException异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class Unsafe {
private static final Unsafe theUnsafe;
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
}

获取Unsafe实例

通过Java命令行命令-Xbootclasspath/a把调用Unsafe相关方法的类所在jar包路径追加到默认的bootstrap路径中,使得调用Unsafe相关方法的类被引导类加载器加载,从而通过Unsafe.getUnsafe方法安全的获取Unsafe实例。

1
java -Xbootclasspath/a:${path}   // 其中path为调用Unsafe相关方法的类所在jar包路径

通过反射获取单例对象theUnsafe

1
2
3
4
5
6
7
8
9
10
public static Unsafe reflectGetUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

Unsafe功能

Unsafe提供的API大致可分为内存操作CASClass相关对象操作线程调度系统信息获取内存屏障数组操作等几类。

Unsafe基本功能

内存操作

主要包含堆外内存分配拷贝释放给定地址值操作等方法。在Java中创建的对象都处于堆内内存,堆内内存是由JVM所管控的进程内存,且遵循JVM的内存管理机制,JVM会采用垃圾回收机制统一管理堆内存堆外内存存在于JVM管控之外的内存区域,对堆外内存的操作,依赖于Unsafe提供的操作堆外内存的native方法

使用堆外内存可对垃圾回收停顿的改善,由于堆外内存是直接受操作系统管理而不是JVM,故使用堆外内存时,即可保持较小的堆内内存规模。从而在GC时减少回收停顿对于应用的影响。

提升程序I/O操作的性能,通常I/O通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝生命周期较短的暂存数据,都建议存储到堆外内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 分配内存, 相当于C++的malloc函数
public native long allocateMemory(long bytes);
// 扩充内存
public native long reallocateMemory(long address, long bytes);
// 释放内存
public native void freeMemory(long address);
// 在给定的内存块中设置值
public native void setMemory(Object o, long offset, long bytes, byte value);
// 内存拷贝
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
// 获取给定地址值,忽略修饰限定符的访问限制。与此类似操作还有: getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
// 为给定地址设置值,忽略修饰限定符的访问限制,与此类似操作还有: putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
public native byte getByte(long address);
// 为给定地址设置byte类型的值(当且仅当该内存地址为allocateMemory分配 时,此方法结果才是确定的)
public native void putByte(long address, byte x);

DirectByteBuffer是Java用于实现堆外内存的一个重要类,其对于堆外内存的创建、使用、销毁等逻辑均由Unsafe提供的堆外内存API来实现,通常用在通信过程中做缓冲池,如Netty、MINA等NIO框架中应用广泛。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
DirectByteBuffer(int cap) {                   // package-private
super(-1, 0, cap, cap);
boolean pa = VM.isDirectMemoryPageAligned();
int ps = Bits.pageSize();
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
Bits.reserveMemory(size, cap);
long base = 0;
try {
base = unsafe.allocateMemory(size); // 分配内存
} catch (OutOfMemoryError x) {
Bits.unreserveMemory(size, cap);
throw x;
}
unsafe.setMemory(base, size, (byte) 0); // 内存初始化
if (pa && (base % ps != 0)) {
// Round up to page boundary
address = base + ps - (base & (ps - 1));
} else {
address = base;
}
// 跟踪DirectByteBuffer对象的垃圾回收,当DirectByteBuffer被垃圾回收时,分配的堆外内存一起被释放
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
att = null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();
long oneHundred = 123456789L;
byte size = 8;
// 调用allocateMemory分配内存
long memoryAddress = unsafe.allocateMemory(size);
System.out.println("memoryAddress: " + memoryAddress);
// 将oneHundred写入到内存中
unsafe.putAddress(memoryAddress, oneHundred);
// 内存中读取数据
long readValue = unsafe.getAddress(memoryAddress);
System.out.println("readValue: " + readValue);
// 释放内存
unsafe.freeMemory(memoryAddress);

CAS

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @param var1 o - 包含要修改field的对象
* @param var2 offset - 对象中某field的偏移量
* @param var4 expected - 期望值
* @param var5 update - 更新值
* @return - true | false
*/
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

通过Unsafe来给属性赋值,offset为user字段的内存偏移地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class UnsafeTest {
private static final Unsafe unsafe;
private static final long offset;
private transient volatile User user;

static {
unsafe = UnsafeInstance.reflectGetUnsafe();
try {
offset = unsafe.objectFieldOffset(UnsafeTest.class.getDeclaredField("head"));
} catch (NoSuchFieldException e) {
throw new RuntimeException();
}
}

public static void main(String[] args) {
UnsafeTest unsafeTest = new UnsafeTest();
User user123 = new User();
user.setAddress("123");
unsafe.compareAndSwapObject(unsafeTest, offset, null, user123);
System.out.println(unsafeTest.head.getAddress());
}
}

线程调度

包括线程挂起恢复锁机制等方法,将一个线程进行挂起是通过park方法实现的,调用park方法后,线程将一直阻塞直到超时或者中断等条件出现;unpark可终止一个挂起的线程,使其恢复正常。

Java锁和同步器框架的核心类AbstractQueuedSynchronizer,就是通过调用LockSupport.park()LockSupport.unpark()实现线程的阻塞和唤醒的,而LockSupport的park、unpark方法实际是调用Unsafe的park、unpark方式来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 取消阻塞线程
public native void unpark(Object thread);
// 阻塞线程
public native void park(boolean isAbsolute, long time);
// 获得对象锁(可重入锁)
@Deprecated
public native void monitorEnter(Object o);
// 释放对象锁
@Deprecated
public native void monitorExit(Object o);
// 尝试获取对象锁
@Deprecated
public native boolean tryMonitorEnter(Object o);
1
2
3
4
5
6
7
8
static Object object = new Object();
static Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();
public void method1(){
unsafe.monitorEnter(object);
}
public void method2(){
unsafe.monitorExit(object);
}
1
2
3
4
5
6
7
8
9
10
11
Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();
Thread t = new Thread(() -> {
System.out.println("thread - is running----");
//true则会实现ms定时,false则会实现ns定时。
unsafe.park(false,0L); //阻塞当前线程
System.out.println("thread is over-----");
});
t.start();
Thread.sleep(1000);
System.out.println("唤醒Thread-t");
unsafe.unpark(t);

内存屏障

Java8中引入,用于定义内存屏障,也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作,避免代码重排序。

1
2
3
4
5
6
// 禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
// 禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
// 禁止load、store操作重排序
public native void fullFence();

Atomic

Atomic包中类基本都是使用Unsafe实现的,Unsafe只提供了三种CAS方法compareAndSwapObjectcompareAndSwapIntcompareAndSwapLong

在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型原子更新数组原子更新引用原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。

基本类AtomicIntegerAtomicLongAtomicBoolean

引用类型AtomicReferenceAtomicReference的ABA实例、AtomicStampedRerenceAtomicMarkableReference

数组类型AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray

属性原子修改器(Updater)AtomicIntegerFieldUpdaterAtomicLongFieldUpdaterAtomicReferenceFieldUpdater