如下sun.misc.Unsafe源碼和demo基於jdk1.7;java
最近在看J.U.C裏的源碼,不少都用到了sun.misc.Unsafe這個類,只知其一;不知其二,看起來總感受有點不盡興,因此打算對Unsafe的源碼及使用作個分析;c++
另外,網上找了份c++的源代碼natUnsafe.cc(惋惜比較老,Copyright (C) 2006, 2007年的,沒找到新的),也就是sun.misc.Unsafe的C++實現,跟Unsafe類中的native方法對照起來看更加容易理解;git
能夠用來在任意內存地址位置處讀寫數據,可見,對於普通用戶來講,使用起來仍是比較危險的;github
另外,還支持一些CAS原子操做;bootstrap
遺憾的是,Unsafe對象不能直接經過new Unsafe()
或調用Unsafe.getUnsafe()
獲取,緣由以下:緩存
*不能直接new Unsafe()
,緣由是Unsafe
被設計成單例模式,構造方法是私有的;ide
*不能經過調用Unsafe.getUnsafe()獲取,由於
thisgetUnsafe
被設計成只能從引導類加載器(bootstrap class loader)加載,從getUnsafe
的源碼中也能夠看出來,以下:
@CallerSensitive public static Unsafe getUnsafe() { //獲得調用該方法的Class對象 Class cc = Reflection.getCallerClass(); //判斷調用該方法的類是不是引導類加載器(bootstrap class loader) //若是不是的話,好比由AppClassLoader調用該方法,則拋出SecurityException異常 if (cc.getClassLoader() != null) throw new SecurityException("Unsafe"); //返回單例對象 return theUnsafe; }
雖然咱們不能經過以上方法獲得Unsafe對象,但得Unsafe類中有個私有的靜態全局屬性theUnsafe(Unsafe實例對象)
,經過反射,能夠獲取到該成員屬性theUnsafe對應的Field對象,並將其設置爲可訪問,從而獲得theUnsafe具體對象,以下代碼:spa
package concurrency; import java.lang.reflect.Field; import sun.misc.Unsafe; import sun.reflect.Reflection; public class Test { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { // 經過反射獲得theUnsafe對應的Field對象 Field field = Unsafe.class.getDeclaredField("theUnsafe"); // 設置該Field爲可訪問 field.setAccessible(true); // 經過Field獲得該Field對應的具體對象,傳入null是由於該Field爲static的 Unsafe unsafe = (Unsafe) field.get(null); System.out.println(unsafe); } }
allocateInstance方法,不調用構造方法生成對象
本地方法,功能是生成一個對象實例,可是不會運行該對象的構造方法;因爲natUnsafe.cc版本較老,沒找到對應的c++實現;線程
/** Allocate an instance but do not run any constructor. Initializes the class if it has not yet been. */ public native Object allocateInstance(Class cls) throws InstantiationException;
例子,利用Unsafe的allocateInstance方法,在未調用構造方法的狀況下生成了對象:
package concurrency; import java.lang.reflect.Field; import sun.misc.Unsafe; import sun.reflect.Reflection; class User { private String name = ""; private int age = 0; public User() { this.name = "test"; this.age = 22; } @Override public String toString() { return name + ": " + age; } } public class Test { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException { // 經過反射獲得theUnsafe對應的Field對象 Field field = Unsafe.class.getDeclaredField("theUnsafe"); // 設置該Field爲可訪問 field.setAccessible(true); // 經過Field獲得該Field對應的具體對象,傳入null是由於該Field爲static的 Unsafe unsafe = (Unsafe) field.get(null); User user = (User) unsafe.allocateInstance(User.class); System.out.println(user); //dont invoke constructor, print null: 0 User userFromNormal = new User(); System.out.println(userFromNormal); //print test: 22 } }
比較簡單,就是返回成員屬性內存地址相對於對象內存地址的偏移量,經過該方法能夠計算一個對象在內存中的空間大小,方法是經過反射獲得它的全部Field(包括父類繼承獲得的),找出Field中偏移量最大值,而後對該最大偏移值填充字節數即爲對象大小;
關於該方法的使用例子能夠看下面的修改內存數據的例子;
這裏,還有put對應的get方法,很簡單就是直接讀取內存地址處的數據,不作舉例;
咱們能夠舉個putLong(Object, long, long)方法詳細看下其具體實現,其它的相似,先看Java的源碼,沒啥好看的,就聲明瞭一個native本地方法:
三個參數說明下:
Object o//對象引用
long offset//對象內存地址的偏移量
long x//寫入的數據
public native void putLong(Object o, long offset, long x);
仍是看下natUnsafe.cc中的c++實現吧,很簡單,就是計算要寫入數據的內存地址,而後寫入數據,以下:
void sun::misc::Unsafe::putLong (jobject obj, jlong offset, jlong value) { jlong *addr = (jlong *) ((char *) obj + offset);//計算要修改的數據的內存地址=對象地址+成員屬性地址偏移量 spinlock lock;//自旋鎖,經過循環來獲取鎖, i386處理器須要加鎖訪問64位數據,若是是int,則不須要改行代碼 *addr = value;//往該內存地址位置直接寫入數據 }
以下例子,即便User類的成員屬性是私有的且沒有提供對外的public方法,咱們仍是能夠直接在它們的內存地址位置處寫入數據,併成功;
package concurrency; import java.lang.reflect.Field; import sun.misc.Unsafe; import sun.reflect.Reflection; class User { private String name = "test"; private long id = 1; private int age = 2; private double height = 1.72; @Override public String toString() { return name + "," + id + "," + age + "," + height; } } public class Test { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException { // 經過反射獲得theUnsafe對應的Field對象 Field field = Unsafe.class.getDeclaredField("theUnsafe"); // 設置該Field爲可訪問 field.setAccessible(true); // 經過Field獲得該Field對應的具體對象,傳入null是由於該Field爲static的 Unsafe unsafe = (Unsafe) field.get(null); User user = new User(); System.out.println(user); //打印test,1,2,1.72 Class userClass = user.getClass(); Field name = userClass.getDeclaredField("name"); Field id = userClass.getDeclaredField("id"); Field age = userClass.getDeclaredField("age"); Field height = userClass.getDeclaredField("height"); //直接往內存地址寫數據 unsafe.putObject(user, unsafe.objectFieldOffset(name), "midified-name"); unsafe.putLong(user, unsafe.objectFieldOffset(id),100l); unsafe.putInt(user, unsafe.objectFieldOffset(age), 101); unsafe.putDouble(user, unsafe.objectFieldOffset(height), 100.1); System.out.println(user);//打印midified-name,100,101,100.1 } }
copyMemory:內存數據拷貝
freeMemory:用於釋放allocateMemory和reallocateMemory申請的內存
看下natUnsafe.cc中的c++實現吧,加深理解,其實就是將內存值與預期值做比較,判斷是否相等,相等的話,寫入數據,不相等不作操做,返回舊數據;
static inline bool compareAndSwap (volatile jint *addr, jint old, jint new_val) { jboolean result = false; spinlock lock; if ((result = (*addr == old))) *addr = new_val; return result; }
J.U.C裏原子類就是基於以上CAS操做實現的;
這類方法使用volatile語義去存取數據,個人理解就是各個線程不緩存數據,直接在內存中讀取數據;
參考鏈接:
https://github.com/aeste/gcc/blob/master/libjava/sun/misc/natUnsafe.cc
http://ifeve.com/sun-misc-unsafe/
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/misc/Unsafe.java