【java】Java 1.8 Unsafe

Unsafe 类在 sun.misc 包下,不属于Java标准。但是很多 Java 的基础类库,以及优秀的三方库都会用这个提升性能。

Unsafe 使用了单例模式,想使用 Unsafe 类就需要获取实例。由于安全限制,不能用一般的方法获取这个实例,通常都是使用反射获取

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UnsafeDemo {
static sun.misc.Unsafe U;

static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
U = (Unsafe) f.get(null);
} catch (Exception e) {
throw new Error(e);
}
}
}

Unsafe 类几类功能:

内存管理

1
long allocateMemory(long bytes)

向操作系统申请一块本地内存,大小为 bytes 字节,返回内存块的地址。本地内存是指不由 JVM 管理的内存,不会被 GC 管理。底层使用 malloc 实现

1
long reallocateMemory(long address, long bytes)

resize 一块本地内存,address 是原内存地址, bytes 是新大小。如果 address 是 0,则内部用 allocateMemory 分配新内存;如果 bytes 是 0,则用 freeMemory 释放原内存。底层使用 realloc 实现。

1
void freeMemory(long address)

释放由 allocateMemory 申请的内存,底层用 free 实现

1
int pageSize()

返回一页内存的大小

1
int addressSize()

返回一个指针的大小

内存操作

内存操作需要提供地址。UnSafe 需要地址的场合会需要两个参数 Object o, long offset

  • 如果 o 是 null, offset 将被视为具体的内存地址
  • 如果 o 非 null,offset 被为字段在对象或数组中的偏移。 o 和 offset 一起得到一个有效地址
1
void setMemory(Object o, long offset, long bytes, byte value)

将一块内存设置为固定值

1
void setMemory(long address, long bytes, byte value)

等价于 setMemory(null, address, bytes, value);

1
void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes)

复制内存

1
void copyMemory(long srcAddress, long destAddress, long bytes)

等价于 copyMemory(null, srcAddress, null, destAddress, bytes)

1
2
T getT(Object o, long offset)
void putT(Object o, long offset, T x)

获取/设置一块内存的值,T 可以是 int、boolean、byte、short、char、long、float、double。

1
2
Object getObject(Object o, long offset)
void putObject(Object o, long offset, Object x)

不需要 Object o 参数的版本,等价于 getT(null, address)

1
2
T getTVolatile(Object o, long offset)
void putTVolatile(Object o, long offset, T x)

带 volatile 语义的 put / get

1
long getAddress(Object o, long offset)

从指定地址获取一个内存指针

1
void putAddress(Object o, long offset, long x)

将指针存到指定地址

1
2
3
void putOrderedObject(Object o, long offset, Object x)
void putOrderedInt(Object o, long offset, int x)
void putOrderedLong(Object o, long offset, long x)

保证写后立即可见

计算类字段的内存偏移位置

1
long staticFieldOffset(Field f)

返回一个静态字段的内存位置

1
long objectFieldOffset(Field f)

返回一个指定字段的存储位置

1
Object staticFieldBase(Field f)

返回一个静态字段的存储位置,是一个引用

非常规的对象实例化

1
Object allocateInstance(Class<?> cls)

创建一个类实例,但不执行构造方法

数组操作

1
int arrayBaseOffset(Class<?> arrayClass)

返回 arrayClass 类型数组的第 0 个元素到数组对象内存开始的偏移

1
int arrayIndexScale(Class<?> arrayClass)

返回一个数组元素需要的长度大小

用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
int base = U.arrayBaseOffset(Integer[].class);
int scale = U.arrayIndexScale(Integer[].class);

final int SIZE = 4;
Integer [] a = new Integer[SIZE];
for (int i = 0 ; i < SIZE ; ++i) {
// 与 a[i] = new Integer(i) 等价
U.putOrderedObject(a, base + i * scale, new Integer(i));
}

for (int i = 0 ; i < SIZE ; ++i) {
System.out.println(a[i]);
}

异常

1
void throwException(Throwable e)

该方法抛出受检异常,但代码不必捕捉或重新抛出它,正如运行时异常一样

动态类

1
Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain)

从字节码创建一个类

1
Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches)

从字节码创建匿名类

1
void ensureClassInitialized(Class<?> c)

加载类

1
boolean shouldBeInitialized(Class<?> c)

检查类是否已加载

多线程同步

1
void monitorEnter(Object obj)

锁定对象

1
void monitorExit(Object obj)

释放锁

monitorEnter 和 monitorExit 的组合相当于 synchronized 关键字

1
boolean tryMonitorEnter(Object obj)

尝试锁定对象

以上 3 个都是 @Deprecated 的方法

原子操作

1
2
3
boolean compareAndSwapObject(Object o, long offset, Object expected, Object x)
boolean compareAndSwapInt(Object o, long offset, int expected, int x)
boolean compareAndSwapLong(Object o, long offset, long expected, long x)

如果变量的值是 expected,则更新成 x,原子更新

1
2
int getAndAddInt(Object o, long offset, int delta)
long getAndAddLong(Object o, long offset, long delta)

原子加

1
2
3
getAndSetInt(Object o, long offset, int newValue)
getAndSetLong(Object o, long offset, long newValue)
getAndSetObject(Object o, long offset, Object newValue)

原子地设置新值和返回旧值

挂起与恢复

1
void park(boolean isAbsolute, long time)

暂停当前线程

1
void unpark(Object thread)

通知线程恢复

JDK concurrent 包的锁大部分用这个实现

内存屏障

1
void loadFence()

在该方法之前的所有读操作,一定在load屏障之前执行完成

1
void storeFence()

在该方法之前的所有写操作,一定在store屏障之前执行完成

1
void fullFence()

在该方法之前的所有读写操作,一定在 full 屏障之前执行完成,这个内存屏障相当于上面两个的合体功能