如下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