从这个类的名字Unsafe
上来说这个类就是一个不安全的类,也是不开放给用户直接使用的(当然我们还是可以通过其他一些方法用到)。
这个类在jdk源码中多个类中用到,主要作用是任意内存地址位置处读写数据,外加一下CAS操作。它的大部分操作都是绕过JVM通过JNI完成的,因此它所分配的内存需要手动free,所以是非常危险的。但是Unsafe中很多(但不是所有)方法都很有用,且有些情况下,除了使用JNI,没有其他方法弄够完成同样的事情。ConcurrentHashMap 和 原子操作CAS等都有用到。
//下面是sun.misc.Unsafe.java类源码
package sun.misc;
import java.lang.reflect.Field;
/*这个类提供了一个更底层的操作并且应该在受信任的代码中使用。可以通过内存地址
存取fields,如果给出的内存地址是无效的那么会有一个不确定的运行表现。*/
public class Unsafe
{
private static final sun.misc.Unsafe theUnsafe; //通过反射获取它可以获得Unsafe实例
private Unsafe(){}
/*获得Unsafe实例,但是这个方法如果我们写的时候调用会报错,因为这个方法在调用时会判断类加载器,
我们的代码是没有“受信任”的,而在jdk源码中调用是没有任何问题的;*/
public static sun.misc.Unsafe getUnsafe() { }
/*
* 返回指定field的在类中内存地址偏移量值,这个值对于给定的field是唯一的,并且后续对该方法的调用都应该返回相同的值。
* @param field 需要返回偏移量的field,一般通过class反射Object.class.getDeclaredField("field")获取该类的字段field
* @return 指定字段field的偏移量*/
public native long objectFieldOffset(Field field);
/*
* 在obj的offset位置中的field值和期望的值比较,如果相同则将update值设置到offset位置中的
* field。这个方法的操作应该是原子的,因此提供了一种不可中断的方式更新field。
* @param obj 包含要修改field的对象
* @param offset obj中整型field的偏移量
* @param expect 希望field中存在的值
* @param update 如果期望值expect与field的当前值相同,设置filed的值为update值
* @return 如果field的值被更改则true,否则为false*/
public native boolean compareAndSwapInt(Object obj, long offset,int expect, int update);
/*
* 设置obj对象中offset偏移地址对应的long型field的值为指定值。支持volatile store语义
* @param obj 包含需要修改field的对象
* @param offset obj中long型field的偏移量
* @param value field将被设置的新值*/
public native void putOrderedLong(Object obj, long offset, long value);
/*
* 设置obj对象中offset偏移地址对应的long型field的值为指定值。支持volatile store语义
* @param obj 包含需要修改field的对象
* @param offset obj中long型field的偏移量
* @param value field将被设置的新值
* @see #putLong(Object,long,long)*/
public native void putLongVolatile(Object obj, long offset, long value);
/*
* 设置obj对象中offset偏移地址对应的long型field的值为指定值。
* @param 包含需要修改field的对象
* @param obj中long型field的偏移量
* @param value field将被设置的新值
* @see #putLongVolatile(Object,long,long) */
public native void putLong(Object obj, long offset, long value);
/*
* 获取obj对象中offset偏移地址对应的long型field的值,支持volatile load语义。
* @param obj 包含需要去读取的field的对象
* @param offset obj中long型field的偏移量
* @see #getLong(Object,long)*/
public native long getLongVolatile(Object obj, long offset);
/*
* 获取obj对象中offset偏移地址对应的long型field的值
* @param obj 包含需要去读取的field的对象
* @param offset obj中long型field的偏移量
* @see #getLongVolatile(Object,long)*/
public native long getLong(Object obj, long offset);
/*
* 获取给定数组中第一个元素的偏移地址。
* 为了存取数组中的元素,这个偏移地址与arrayIndexScale方法的非0返回值一起被使用。
* @param arrayClass 第一个元素地址被获取的class
* @return 数组第一个元素 的偏移地址
* @see arrayIndexScale(Class)*/
public native int arrayBaseOffset(Class arrayClass);
/*
* 获取用户给定数组寻址的增量地址.一个合适的换算因子不能返回的时候(例如:基本类型),
* 返回0.这个返回值能够与arrayBaseOffset一起使用去存取这个数组class中的元素
* @param 应该被返回的数组类
* @return 返回增量地址, 如果结束返回0
*/
public native int arrayIndexScale(Class arrayClass);
/*
* 释放被park(挂起)创建的在一个线程上的阻塞.这个
* 方法也可以被使用来终止一个先前调用park导致的阻塞.
* 这个操作操作时不安全的,因此线程必须保证是活的.这是java代码不是native代码。
* @param thread 要解除阻塞的线程*/
public native void unpark(Thread thread);
/*
* 阻塞一个线程直到unpark出现、线程
* 被中断或者timeout时间到期。如果一个unpark调用已经出现了,
* 这里只计数。timeout为0表示永不过期.当isAbsolute为true时,
* timeout是相对于新纪元之后的毫秒。否则这个值就是超时前的纳秒数。这个方法执行时
* 也可能不合理地返回(没有具体原因)
* @param isAbsolute 如果为true timeout的值是一个相对于新纪元之后的毫秒数
* @param time 可以是一个要等待的纳秒数,或者是一个相对于新纪元之后的毫秒数直到到达这个时间点
*/
public native void park(boolean isAbsolute, long time);
}
当然里面方法很多都是重复的还有很多不仅常用的,这里只写一部分,还有内存相关的
public native long allocateMemory(long l);
public native long reallocateMemory(long l, long l1);
public native void freeMemory(long l);
类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应。Unsafe的直接内存访问:用Unsafe开辟的内存空间不占用Heap空间,当然也不具有自动内存回收功能。做到像C一样自由利用系统内存资源。应用的话看这篇
2、下面是对它的总结:
Unsafe的大部分API都是native的方法,主要包括以下几类:
1)Class相关。主要提供Class和它的静态字段的操作方法。
2)Object相关。主要提供Object和它的字段的操作方法。
3)Arrray相关。主要提供数组及其中元素的操作方法。
4)并发相关。主要提供低级别同步原语,如CAS、线程调度、volatile、内存屏障等。
5)Memory相关。提供了直接内存访问方法(绕过Java堆直接操作本地内存),可做到像C一样自由利用系统内存资源。
6)系统相关。主要返回某些低级别的内存信息,如地址大小、内存页大小。
2.1Class相关
//静态属性的偏移量,用于在对应的Class对象中读写静态属性
public native long staticFieldOffset(Field f);
public native long objectFieldOffset(Field f);
public native Object staticFieldBase(Field f);
//判断是否需要初始化一个类
public native boolean shouldBeInitialized(Class<?> c);
//确保类被初始化
public native void ensureClassInitialized(Class<?> c);
//定义一个类,可用于动态创建类
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);
2.2Object相关
Java中的基本类型(boolean、byte、char、short、int、long、float、double)及对象引用类型都有以下方法。
//获得对象的字段偏移量
public native long objectFieldOffset(Field f);
//获得给定对象地址偏移量的int值
public native int getInt(Object o, long offset);
//设置给定对象地址偏移量的int值
public native void putInt(Object o, long offset, int x);
//创建对象,但并不会调用其构造方法。如果类未被初始化,将初始化类。
public native Object allocateInstance(Class<?> cls) throws InstantiationException;
2.3数组相关
//返回数组中第一个元素的偏移地址
public native int arrayBaseOffset(Class<?> arrayClass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
public static final int ARRAY_BOOLEAN_BASE_OFFSET
= theUnsafe.arrayBaseOffset(boolean[].class);
//返回数组中每一个元素占用的大小
public native int arrayIndexScale(Class<?> arrayClass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
public static final int ARRAY_BOOLEAN_INDEX_SCALE
= theUnsafe.arrayIndexScale(boolean[].class);
通过arrayBaseOffset和arrayIndexScale可定位数组中每个元素在内存中的位置。
2.4并发相关
2.4.1CAS相关
CAS:CompareAndSwap,内存偏移地址offset,预期值expected,新值x。如果变量在当前时刻的值和预期值expected相等,尝试将变量的值更新为x。如果更新成功,返回true;否则,返回false。
//更新变量值为x,如果当前值为expected
//o:对象 offset:偏移量 expected:期望值 x:新值
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);
从Java 8开始,Unsafe中提供了以下方法:
//增加
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
public final long getAndAddLong(Object o, long offset, long delta) {
long v;
do {
v = getLongVolatile(o, offset);
} while (!compareAndSwapLong(o, offset, v, v + delta));
return v;
}
//设置
public final int getAndSetInt(Object o, long offset, int newValue) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, newValue));
return v;
}
public final long getAndSetLong(Object o, long offset, long newValue) {
long v;
do {
v = getLongVolatile(o, offset);
} while (!compareAndSwapLong(o, offset, v, newValue));
return v;
}
public final Object getAndSetObject(Object o, long offset, Object newValue) {
Object v;
do {
v = getObjectVolatile(o, offset);
} while (!compareAndSwapObject(o, offset, v, newValue));
return v;
}
2.4.2线程调度相关
//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程
public native void park(boolean isAbsolute, long time);
//获得对象锁
public native void monitorEnter(Object o);
//释放对象锁
public native void monitorExit(Object o);
//尝试获取对象锁,返回true或false表示是否获取成功
public native boolean tryMonitorEnter(Object o);
2.4.3volatile相关读写
Java中的基本类型(boolean、byte、char、short、int、long、float、double)及对象引用类型都有以下方法。
//从对象的指定偏移量处获取变量的引用,使用volatile的加载语义
//相当于getObject(Object, long)的volatile版本
public native Object getObjectVolatile(Object o, long offset);
//存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义
//相当于putObject(Object, long, Object)的volatile版本
public native void putObjectVolatile(Object o, long offset, Object 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);
2.4.4内存屏障相关
Java 8引入 ,用于定义内存屏障,避免代码重排序。
//内存屏障,禁止load操作重排序,即屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
//内存屏障,禁止store操作重排序,即屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
//内存屏障,禁止load、store操作重排序
public native void fullFence();
2.5直接内存访问(非堆内存)
allocateMemory所分配的内存需要手动free(不被GC回收)
//(boolean、byte、char、short、int、long、float、double)都有以下get、put两个方法。
//获得给定地址上的int值
public native int getInt(long address);
//设置给定地址上的int值
public native void putInt(long address, int x);
//获得本地指针
public native long getAddress(long address);
//存储本地指针到给定的内存地址
public native void putAddress(long address, long x);
//分配内存
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) {
setMemory(null, address, bytes, value);
}
//内存内容拷贝
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset,long bytes);
//内存内容拷贝
public void copyMemory(long srcAddress, long destAddress, long bytes) {
copyMemory(null, srcAddress, null, destAddress, bytes);
}
//释放内存
public native void freeMemory(long address);
2.6系统相关
//返回指针的大小。返回值为4或8。
public native int addressSize();
public static final int ADDRESS_SIZE = theUnsafe.addressSize();
//内存页的大小。
public native int pageSize();