一. 简介
sun.misc包下的一个类, 可以像C语言一样直接使用指针进行访问内存资源。
1.1 获取方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final class Unsafe {
// 单例对象
private static final Unsafe theUnsafe;
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
// 仅在引导类加载器`BootstrapClassLoader`加载时才合法
if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
}
由于包sun.misc位于${JAVA_HOME}/lib/rt.jar中, 属于引导类加载器的加载范围,不能直接使用,于是要采用反射的方式进行调用。
1
2
3
4
5
6
7
8
9
10
private static Unsafe reflectGetUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
二. 功能
2.1 内存操作
堆外内存操作API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//分配内存, 相当于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);
//获取给定地址的byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果为确定的)
public native byte getByte(long address);
//为给定地址设置byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果才是确定的)
public native void putByte(long address, byte x);
Unsafe提供的堆外内存操作API, Java中操作堆外内存使用的都是这个Unsafe进行操作
2.1.1 代表应用
DirectByteBuffer, java中堆外内存的使用。在Netty, MINA等Nio框架中都有使用, 里面的实现都是通过Unsafe进行实现。
DirectByteBuffer的创建和初始化都是使用Unsafe进行分配和初始内存, 通过Cleaner进行追踪DirectByteBuffer对象的垃圾回收情况, 并在回收DirectByteBuffer的时候释放堆外内存。
Cleaner释放堆外内存的原理:
cleaner是一个phantomreference, 不影响引用对象的生命周期,在对象被回收的时候, 会让对象引用(Cleaner)进入pending链表,然后执行对象引用的clean方法。
2.2 CAS操作
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* CAS
* @param o 包含要修改field的对象
* @param offset 对象中某field的偏移量
* @param expected 期望值
* @param update 更新值
* @return true | false
*/
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object update);
public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);
CAS的底层实现为什么能保证其原子性呢?实际上CAS的底层操作是通过给CPU总线进行加锁, 令其他的物理处理器无法访问这一块数据, 保证了并发的时候, 只有一个物理处理器在处理这个数据, 从而保证了原子性。
2.3 线程调度
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);
2.4 对象操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//返回对象成员属性在内存地址相对于此对象的内存地址的偏移量
public native long objectFieldOffset(Field f);
//获得给定对象的指定地址偏移量的值,与此类似操作还有: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);
//从对象的指定偏移量处获取变量的引用,使用volatile的加载语义
public native Object getObjectVolatile(Object o, long offset);
//存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义
public native void putObjectVolatile(Object o, long offset, Object x);
//有序、延迟版本的putObjectVolatile方法,不保证值的改变被其他线程立即看到。只有在field被volatile修饰符修饰时有效
public native void putOrderedObject(Object o, long offset, Object x);
//绕过构造方法、初始化代码来创建对象
public native Object allocateInstance(Class<?> cls) throws InstantiationException;