`

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的安全检查错误

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

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

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

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

    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