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)); } }
《参考文献》
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
Disruptor项目
《Java Magic. Part 4: sun.misc.Unsafe》
《Java内存访问重排序的研究》