Unsafe类包装了很多底层的、非安全的操作。虽然该类及其所有的方法都是public的,但是它只能被受信任的代码使用,并发框架中的很多类,以及Disruptor框架都是使用了Unsafe类。


Unsafe类可以做什么

Unsafe类中的方法基本都是native方法,使用该类可以直接操作内存中的数据,具体来讲,功能可以分为如下几类:

直接内存操作,如分配、读写、释放内存

public native long allocateMemory(long bytes);

public native long reallocateMemory(long address, long bytes);

public native void setMemory(Object o, long offset, long bytes, byte value);

public void setMemory(long address, long bytes, byte value);

public native void copyMemory(Object srcBase, long srcOffset,
                                  Object destBase, long destOffset,
                                  long bytes);
                                  
public void copyMemory(long srcAddress, long destAddress, long bytes);

public native void freeMemory(long address);

public native int addressSize();

public native int pageSize();

定位对象的字段在内存中偏移量

public native long staticFieldOffset(Field f);

public native long objectFieldOffset(Field f);

public native Object staticFieldBase(Field f);

public native int arrayBaseOffset(Class arrayClass);

public static final int INVALID_FIELD_OFFSET   = -1;

public static final int ARRAY_BOOLEAN_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(boolean[].class);
            
public static final int ARRAY_BYTE_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(byte[].class);
            
public static final int ARRAY_SHORT_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(short[].class);
            
public static final int ARRAY_CHAR_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(char[].class);
            
public static final int ARRAY_INT_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(int[].class);
            
public static final int ARRAY_LONG_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(long[].class);
            
public static final int ARRAY_FLOAT_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(float[].class);
            
public static final int ARRAY_DOUBLE_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(double[].class);
            
public static final int ARRAY_OBJECT_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(Object[].class);

读写volatile对象

这里说明一下putOrderedxxx,按照注释,

This is an ordered or lazy version of <code>putIntVolatile(Object,long,int)</code>, which doesn't guarantee the immediate visibility of the change to other threads. It is only really useful where the integer field is volatile, and is thus expected to change unexpectedly.

这是一个有序或者有延迟的putIntVolatile方法,并且不保证值的改变被其他线程立即看到。只有在field被volatile修饰并且期望被意外修改的时候使用才有用。

简单点说,就是使用这样的方法写入volatile对象只能够避免写重排序,但使volatile失去可见性,这样的技巧可以在某些场景下提高效率。

public native Object getObjectVolatile(Object o, long offset);

public native void    putObjectVolatile(Object o, long offset, Object x);

public native int     getIntVolatile(Object o, long offset);

public native void    putIntVolatile(Object o, long offset, int x);

public native boolean getBooleanVolatile(Object o, long offset);

public native void    putBooleanVolatile(Object o, long offset, boolean x);

public native byte    getByteVolatile(Object o, long offset);

public native void    putByteVolatile(Object o, long offset, byte x);

public native short   getShortVolatile(Object o, long offset);

public native void    putShortVolatile(Object o, long offset, short x);

public native char    getCharVolatile(Object o, long offset);

public native void    putCharVolatile(Object o, long offset, char x);

public native long    getLongVolatile(Object o, long offset);

public native void    putLongVolatile(Object o, long offset, long x);

public native float   getFloatVolatile(Object o, long offset);

public native void    putFloatVolatile(Object o, long offset, float x);

public native double  getDoubleVolatile(Object o, long offset);

public native void    putDoubleVolatile(Object o, long offset, double x);

public native void    putOrderedObject(Object o, long offset, Object x);

public native void    putOrderedInt(Object o, long offset, int x);

public native void    putOrderedLong(Object o, long offset, long x);

读写普通对象

public native byte    getByte(long address);

public native void    putByte(long address, byte x);

public native short   getShort(long address);

public native void    putShort(long address, short x);

public native char    getChar(long address);

public native void    putChar(long address, char x);

public native int     getInt(long address);

public native void    putInt(long address, int x);

public native long    getLong(long address);

public native void    putLong(long address, long x);

public native float   getFloat(long address);

public native void    putFloat(long address, float x);

public native double  getDouble(long address);

public native void    putDouble(long address, double x);

public native int getInt(Object o, long offset);

public native void putInt(Object o, long offset, int x);

public native Object getObject(Object o, long offset);

public native void putObject(Object o, long offset, Object x);

public native boolean getBoolean(Object o, long offset);

public native void    putBoolean(Object o, long offset, boolean x);

public native byte    getByte(Object o, long offset);

public native void    putByte(Object o, long offset, byte x);

public native short   getShort(Object o, long offset);

public native void    putShort(Object o, long offset, short x);

public native char    getChar(Object o, long offset);

public native void    putChar(Object o, long offset, char x);

public native long    getLong(Object o, long offset);

public native void    putLong(Object o, long offset, long x);

public native float   getFloat(Object o, long offset);

public native void    putFloat(Object o, long offset, float x);

public native double  getDouble(Object o, long offset);

public native void    putDouble(Object o, long offset, double x);

CAS操作

public final native boolean compareAndSwapObject(Object o, long offset,
                                                     Object expected,
                                                     Object x);

public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);
                                                  
public final native boolean compareAndSwapLong(Object o, long offset,
                                                   long expected,
                                                   long x);

定义类,分配实例

public native Class defineClass(String name, byte[] b, int off, int len,
                                    ClassLoader loader,
                                    ProtectionDomain protectionDomain);

public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches);

public native Object allocateInstance(Class cls) throws InstantiationException;

内置锁操作

public native void monitorEnter(Object o);

public native void monitorExit(Object o);

public native boolean tryMonitorEnter(Object o);

抛出异常

public native void throwException(Throwable ee);

挂起与恢复线程

public native void park(boolean isAbsolute, long time);

public native void unpark(Object thread);


获取Unsafe类的实例

Unsafe类是一个饿汉式单例类,提供的获取实例的方法又对调用的类的类加载器进行了校验

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class cc = Reflection.getCallerClass();
        if (cc.getClassLoader() != null)
            throw new SecurityException("Unsafe");
        return theUnsafe;
    }

所以不能直接获取类的实例,我们可以让我们的代码受信,运行程序时,使用bootclasspath 选项,指定系统类路径加上你使用的一个Unsafe路径。

java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient

可以用如下代码获取Unsafe类的实例,以下代码改写自Disruptor-3.3.2的源码

import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;

import sun.misc.Unsafe;

public class TestUnsafe {
    public static Unsafe getUnsafe() {
        Unsafe unsafe = null;

        try {
            final PrivilegedExceptionAction<Unsafe> action = new PrivilegedExceptionAction<Unsafe>() {
                public Unsafe run() throws Exception {
                    Field theUnsafe = Unsafe.class
                            .getDeclaredField("theUnsafe");
                    theUnsafe.setAccessible(true);
                    return (Unsafe) theUnsafe.get(null);
                }
            };

            unsafe = AccessController.doPrivileged(action);
        }
        catch (Exception e) {
            throw new RuntimeException("Unable to load unsafe", e);
        }
        
        return unsafe;
    }

    public static void main(String[] args) {
        System.out.println(getUnsafe().arrayIndexScale(Object[].class));
    }
}


《参考文献》

  1. Unsafe源码

    https://jdk7.java.net/source.html

    openjdk-7u40-fcs-src-b43-26_aug_2013.zip\openjdk\jdk\src\share\classes\sun\misc\Unsafe.java

  2. Disruptor项目

    http://lmax-exchange.github.io/disruptor/

  3. 《Java Magic. Part 4: sun.misc.Unsafe》

    http://ifeve.com/sun-misc-unsafe/

  4. 《Java内存访问重排序的研究》

    http://tech.meituan.com/java-memory-reordering.html