Java的Unsafe類

Unsafe類包裝了不少底層的、非安全的操做。雖然該類及其全部的方法都是public的,可是它只能被受信任的代碼使用,併發框架中的不少類,以及Disruptor框架都是使用了Unsafe類。html


Unsafe類能夠作什麼java

Unsafe類中的方法基本都是native方法,使用該類能夠直接操做內存中的數據,具體來說,功能能夠分爲以下幾類:
git

直接內存操做,如分配、讀寫、釋放內存github

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.ide

這是一個有序或者有延遲的putIntVolatile方法,而且不保證值的改變被其餘線程當即看到。只有在field被volatile修飾而且指望被意外修改的時候使用纔有用。spa

簡單點說,就是使用這樣的方法寫入volatile對象只可以避免寫重排序,但使volatile失去可見性,這樣的技巧能夠在某些場景下提升效率。.net

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

相關文章
相關標籤/搜索