`

Unsafe使用初探

jdk 
阅读更多

在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();
}

 

 

分享到:
评论

相关推荐

    CS使用Unsafe的方法向DLL传参

    CS使用Unsafe的方法向DLL传参 由一个实例开始: c++中函数声明如下: bool Test(float** arr,int rows,int cols); 这个函数在C#中怎么使用,直接传入single[,]出现错误”尝试读取或写入受保护的内存",那到底...

    JDK8中sun.misc下UnSafe类源代码 UnSafe.java

    JDK8中sun.misc下UnSafe类源代码 UnSafe.java JDK8中sun.misc下UnSafe类源代码 UnSafe.java

    netty-unsafe总结

    对Netty中的Unsafe做了简单的总结,构建自己的知识网!!!

    JDK Unsafe 源码注释

    并发作为 Java 中非常重要的一部分,其内部大量使用了 Unsafe 类,它为 java.util.concurrent 包中的类提供了底层支持。

    unsafe:使用sun.misc.Unsafe的各种Java类

    unsafe-helper-包含一些简单的方法,这些方法使使用sun.misc.Unsafe更容易。 unsafe-collection-在ArrayList上建模的示例列表,该列表不存储对集合内对象的引用,而是直接将元素复制到列表中。 这有一些有趣的特性...

    Java Unsafe类的使用.docx

    CAS算法的出现使得在不使用synchronize这种“悲观锁”依然可以实现数据的安全访问,CAS算法是指先读取要修改的变量值,对它进行计算,然后执行检查并更新这个步骤(更新前判断那个值是否是之前那个读到的值),检查...

    sun.misc.Unsafe源码

    sun.misc.Unsafe源码文件,需要学习的带走。希望能够帮助到大家。

    JDK8中sun.misc包下的UnSafe类

    JDK8中sun.misc包下的UnSafe类,想查看源码的就拿走,没积分的请与我联系!xtfggef@gmail.com

    解决VS中This function or variable may be unsafe的安全检查错误.docx

    解决VS中This function or variable may be unsafe的安全检查错误

    解决VS中This function or variable may be unsafe的安全检查错误.pdf

    解决VS中This function or variable may be unsafe的安全检查错误

    Java为什么会引入及如何使用Unsafe

    但是究竟是什么取代Unsafe不得而知,个人推测会有不止一样来取代它,那么问题来了,到底为什么要使用Unsafe?  做一些Java语言不允许但是又十分有用的事情  很多低级语言中可用的技巧在Java中都是不被允许的。对...

    Java Unsafe类1

    Java Unsafe类1

    Memory.Unsafe:使用非托管内存和指针的不安全方法

    以后已修复此问题(版本&gt; = 0.2.2.0),但是这也意味着一直通过DotNetCross.Memory.Unsafe.dll引用该程序包的DotNetCross.Memory.Unsafe.dll可能不知道将其更改为DotNetCross.Memory.Unsafe 。 不幸的是,无法...

    Go unsafe 包的使用详解

    go官方是不推荐使用unsafe的操作因为它是不安全的,它绕过了golang的内存安全原则,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护。但是在很多地方却是很实用。在一些go底层的包中unsafe包被很频繁的...

    Java Unsafe类实现原理及测试代码

    过度的使用Unsafe类会使得出错的几率变大,因此Java官方并不建议使用的,官方文档也几乎没有。Oracle正在计划从Java 9中去掉Unsafe类,如果真是如此影响就太大了。 Unsafe类提供了以下这些功能: 一、内存管理。包括...

    unsafe:是否想尝试Rust不安全{}? 仍然坚持使用Ruby? 这个给你

    仍然坚持使用Ruby? 这个给你。 一颗宝石中的所有善良 没有垃圾收集器 空指针 不确定。 用法 #!/usr/bin/env ruby # -- encoding: utf-8 -- require 'unsafe/unsafe' CHANCE_OF_DOOM = 0.5 unsafe { ( 1 .. 10 ) ...

    go中的unsafe包及使用详解

    Unsafe code是一种绕过go类型安全和内存安全检查的Go代码。这篇文章主要介绍了go中的unsafe包,需要的朋友可以参考下

    java魔法类:Unsafe应用

    java魔法类:Unsafe应用

    golang-标准库(unsafe)

    要使用unsafe包需要先了解内存对齐,对有c使用经验的朋友就比较简单了。数据在内存的存储并不是连续按着存放的,而是按照一定的对齐规则。使用对齐规则有一个最大的好处是避免cpu的二次读取,也就是说对齐后的数据...

    java Unsafe详细解析

    Unsafe为我们提供了访问底层的机制,这种机制仅供java核心类库使用,而不应该被普通用户使用。但是,为了更好地了解java的生态体系,我们应该去学习它,去了解它,不求深入到底层的C/C++代码,但求能了解它的基本...

Global site tag (gtag.js) - Google Analytics