解析与实战JDK Unsafe
说到锁, 无锁队列, 那么就不能不说 sun.misc.Unsafe , juc底层的操作很多都是依赖于Unsafe提供的方法. 事实上它能做的事情远远多于这些.
在我的github上我写了几个示例代码, 来说明它的各种用法. 请移步这里
1. Unsafe的初始化
为了防止程序员误用, Unsafe的构造器声明为私有. 并且静态方法Unsafe.getUnsafe()会检查类加载器是否是BootstrapClassLoader, 否则抛出SecurityException
public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
- 我们可以指定使用bootstrap来加载我们的class, 但是这样做不方便. java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:
- 我们可以通过Reflecttion获得Unsafe中的私有成员变量theUnsafe
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
=============================================
2. Unsafe中的API
Unsafe中有105个方法, 我将其中重要的几类列下来:
- Info. 返回底层内存信息
- addressSize
- pageSize
- Objects. 操作对象和field
- allocateInstance
- objectFieldOffset
- Classes. 操作类和静态变量
- staticFieldOffset
- defineClass
- defineAnonymousClass
- ensureClassInitialized
- Arrays. 操作数组
- arrayBaseOffset
- arrayIndexScale
- Synchronization 底层同步操作
- monitorEnter
- tryMonitorEnter
- monitorExit
- compareAndSwapInt
- putOrderedInt
- Memory. 直接操作内存
- allocateMemory
- copyMemory
- freeMemory
- getAddress
- getInt
- putInt
3. 有趣的case
1. 防止初始化
使用allocateInstance(), 可以防止对象初始化. 包括field的初始化和构造器
- 需要跳过对象初始化阶段
- 绕过含安全检查的构造函数
- 想要的获得类的实例,但它没有任何公开的构造器
class A {
private long a; // not initialized value
public A() {
this.a = 1; // initialization
}
public long a() { return this.a; }
}
A o1 = new A(); // constructor
o1.a(); // prints 1
A o2 = A.class.newInstance(); // reflection
o2.a(); // prints 1
A o3 = (A) unsafe.allocateInstance(A.class); // unsafe
o3.a(); // prints 0
2. 内存填充/替换
使用putInt可以将指定位置的内存进行填充和替换, 与Reflection的区别是: 反射必须指定对象, 而Unsafe只需要指定内存地址
unsafe.putInt(obj, 16 + unsafe.objectFieldOffset(f), 42); // obj对象的大小在32位系统中占16位, 故可以将相邻的obj中字段进行赋值
3. 获得对象大小
使用objectFieldOffset可以获得对象大小. (shallow size 并非真实的占用内存大小)
遍历所有非静态变量, 包括父类. 获得每个变量的offset, 找到最大的offset. 并增加padding(内存对齐. 按8位进行对齐)
public static long sizeOf(Object o) {
Unsafe u = getUnsafe();
HashSet<Field> fields = new HashSet<Field>();
Class c = o.getClass();
while (c != Object.class) {
for (Field f : c.getDeclaredFields()) {
if ((f.getModifiers() & Modifier.STATIC) == 0) {
fields.add(f);
}
}
c = c.getSuperclass();
}
// get offset
long maxSize = 0;
for (Field f : fields) {
long offset = u.objectFieldOffset(f);
if (offset > maxSize) {
maxSize = offset;
}
}
return ((maxSize/8) + 1) * 8; // padding, 8位对齐
}
4. 并发
juc中的Atomic, ConcurrentLinkedQueue, ReentrantLock的底层实现都有Unsafe的参与, 主要是使用了两个方法, objectFieldOffset和compareAndSwapObject
Counter counter = new Counter() {
private volatile long counter = 0;
private Unsafe unsafe;
private long offset;
{
unsafe = UnSafeFactory.getInstance();
offset = unsafe.objectFieldOffset(this.getClass().getDeclaredField("counter"));
}
@Override
public void increment() {
long before = counter;
unsafe.getAndAddLong(this, offset, 1L); //jdk1.8优化, 无需loop
// while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) {
// before = counter;
// }
}
@Override
public long getCounter() {
return counter;
}
};
//以下是从OpenJDK copy来的, 保留行号.
1047 public final long getAndAddLong(Object o, long offset, long delta) {
1048 long v;
1049 do {
1050 v = getLongVolatile(o, offset);
1051 } while (!compareAndSwapLong(o, offset, v, v + delta));
1052 return v;
1053 }
关于cas的参考资料: