在jdk源码中,经常能够看到sun.misc.Unsafe的使用,通过Unsafe可以操作内存管理等相关操作。
1.怎么使用Unsafe?
public final class Unsafe{
private static final Unsafe theUnsafe;
private Unsafe(){}
public static Unsafe getUnsafe(){
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
}
从Unsafe类中可以看出该类并不推荐让外部用户来使用,首先构造函数为私有的,外部无法通过new实例来使用该类,其次静态的getUnsafe方法的使用也有局限,类加载器必须是bootstrap时,才会返回对应实例。
那该怎么办?还好可以通过反射来获取Unsafe的实例,代码如下:
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe)field.get(null);
2.获取到unsafe实例后,下面主要介绍常用的功能
(1)修改对象的属性值
这个在开发过程中经常会用到,通常的做法是通过反射来进行设置,现在使用unsafe也可以做到这一点。具体操作用到unsafe的objectFieldOffset方法,它返回成员属性在内存中的地址相对于对象内存地址的偏移量。通过该方法可以计算一个对象在内存中的空间大小,找出Field中偏移量最大值,然后对该最大偏移值填充字节数即为对象大小。下面比较反射以及unsafe在修改对象属性值的性能
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* Created by yuanchen.xuyc on 2016/7/8.
* 测试反射和unsafe
*/
public class TestUnsafe {
static Field fieldA;
static Field fieldB;
static long aFieldOffset;
static long bFieldOffset;
static Unsafe unsafe;
static long loopSize = 50000000;
static long preHot = 3000;
static {
try {
//1.反射
fieldA = Bean.class.getDeclaredField("a");
fieldB = Bean.class.getDeclaredField("b");
fieldA.setAccessible(true);
fieldB.setAccessible(true);
//获取unsafe实例
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe)field.get(null);
//获取属性a,b的偏移量
aFieldOffset = unsafe.objectFieldOffset(fieldA);
bFieldOffset = unsafe.objectFieldOffset(fieldB);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String args[]) throws IllegalAccessException {
testReflection();
testUnsafe();
testDirect();
}
static class Bean{
private int a;
private int b;
}
private static void testReflection() throws IllegalAccessException {
Bean bean = new Bean();
//预热
for(int i=0; i<preHot;i++){
fieldA.set(bean,i);
fieldB.set(bean,i);
}
long start = System.currentTimeMillis();
for(int i=0; i<loopSize;i++){
fieldA.set(bean,i);
fieldB.set(bean,i);
}
long end = System.currentTimeMillis();
System.out.println("反射值set耗费:"+(end-start)+"ms");
}
private static void testUnsafe(){
Bean bean = new Bean();
//预热
for(int i=0; i<preHot;i++){
unsafe.putInt(bean, aFieldOffset, i);
unsafe.putInt(bean, bFieldOffset, i);
}
long start = System.currentTimeMillis();
for(int i=0; i<loopSize;i++){
unsafe.putInt(bean, aFieldOffset, i);
unsafe.putInt(bean, bFieldOffset, i);
}
long end = System.currentTimeMillis();
System.out.println("unsafe set耗费:"+(end-start)+"ms");
}
private static void testDirect(){
Bean bean = new Bean();
for(int i=0; i<preHot;i++){
bean.a = i;
bean.b = i;
}
long start = System.currentTimeMillis();
for(int i=0; i<loopSize;i++){
bean.a = i;
bean.b = i;
}
long end = System.currentTimeMillis();
System.out.println("direct set耗费:"+(end-start)+"ms");
}
}
在x240机器上测试的结果如下:
反射值set耗费:2697ms
unsafe set耗费:164ms
direct set耗费:100ms
从数据上可以看出,unsafe的耗时接近于直接赋值,而反射的性能和unsafe比起来不在一个数量级上。
(2)提供了compareAndSwap,用于实现无锁计数功能
直接上代码,比较几种计数的效率
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Created by yuanchen.xuyc on 2016/7/8.
*/
public class TestCouter {
private static int threadNum = 500;
private static int num_to_increment = 1000000;
public static void main(String args[]) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
MyCounter synCounter = new SyncCounter();
MyCounter lockCounter = new LockCounter();
MyCounter atomicCounter = new AtomicCounter();
MyCounter unsafeCounter = new UnsafeCounter();
long startTime = System.currentTimeMillis();
for(int i=0;i<threadNum;i++){
executorService.submit(new Count(synCounter,num_to_increment));
}
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
long endTime = System.currentTimeMillis();
System.out.println("计数到:"+synCounter.getCounter());
System.out.println("计数耗时:"+(endTime-startTime) + "ms");
}
}
class Count implements Runnable{
private MyCounter myCounter;
private int count;
Count(MyCounter myCounter,int count){
this.myCounter = myCounter;
this.count = count;
}
public long getCounter(){
return myCounter.getCounter();
}
public void run() {
for(int i=0;i<count;i++) {
myCounter.increment();
}
}
}
/**
* 利用synchronized加锁实现同步
*/
class SyncCounter extends MyCounter{
private long counter = 0l;
public synchronized void increment(){
counter++;
}
public long getCounter(){
return counter;
}
}
/**
* 使用读写锁实现
*/
class LockCounter extends MyCounter{
private ReentrantReadWriteLock.WriteLock lock = new ReentrantReadWriteLock().writeLock();
private long counter = 0l;
public void increment(){
try{
lock.lock();
counter++;
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public long getCounter(){
return counter;
}
}
/**
* 使用原子类
*/
class AtomicCounter extends MyCounter{
private AtomicLong atomicLong = new AtomicLong(0);
public void increment(){
atomicLong.incrementAndGet();
}
public long getCounter(){
return atomicLong.get();
}
}
/**
* 使用unsafe
*/
class UnsafeCounter extends MyCounter{
private volatile long counter = 0l;
private Unsafe unsafe;
private long offset;
private Unsafe getUnsafe(){
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe)field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
return unsafe;
}
UnsafeCounter(){
unsafe = getUnsafe();
try {
offset = unsafe.objectFieldOffset(UnsafeCounter.class.getDeclaredField("counter"));
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
public void increment(){
long before = counter;
while(!unsafe.compareAndSwapLong(this,offset,before,before+1)){
before = counter;
}
}
public long getCounter(){
return counter;
}
}
abstract class MyCounter{
abstract void increment();
abstract long getCounter();
}