通过反射来使用unsafe包的方法

通过反射来使用unsafe包的方法

 

public class GetUnsafeInstance {
    public static Unsafe getUnsafeInstance() {
        try {
            Class<?> clazz = Unsafe.class;
            Field f = clazz.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe = (Unsafe) f.get(clazz);
            return unsafe;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return null;
    }
}


反射、Unsafe、直接调用性能大比拼

 在java动态表达式、原生调用、反射性能对比这一篇的基础上,本文我们将比较原生调用和直接通过Unsafe指针操作java对象的性能。

复制代码
package com.hundsun.jrescloud;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @author zjhua
 * @description
 * @date 2020/1/26 20:20
 */
public class TestUnSafe {
    static final Unsafe unsafe;
    static final long stateOffset;
    static final long volStateOffset;
    private volatile long volState = 0;
    private long state = 0;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");   # 对于不在rt.jar下的类,只能通过反射获取Unsafe实例,否则会发生安全越界
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
            stateOffset = unsafe.objectFieldOffset(TestUnSafe.class.getDeclaredField("state"));
            volStateOffset = unsafe.objectFieldOffset(TestUnSafe.class.getDeclaredField("volState"));
        } catch (Exception ex) {
            System.out.println(ex.getLocalizedMessage());
            throw new Error(ex);
        }
    }

    public static void main(String[] args) {
        TestUnSafe test = new TestUnSafe();
        long beg = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            unsafe.putLong(test, stateOffset,i);
        }
        System.out.println("unsafe put non volatile: " + (System.currentTimeMillis() - beg));
        beg = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            test.state = i;
        }
        System.out.println("pojo put non volatile: " + (System.currentTimeMillis() - beg));

        beg = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            unsafe.putLong(test, stateOffset,i);
        }
        System.out.println("unsafe put volatile: " + (System.currentTimeMillis() - beg));
        beg = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            test.volState = i;
        }
        System.out.println("pojo put volatile: " + (System.currentTimeMillis() - beg));
    }
}
复制代码

  输出如下:

unsafe put non volatile: 179  // 普通属性,直接操作更快。如果使用setter的话,则有时候unsafe快,有时候pojo快。按说unsafe直接通过指针操作,至少不应该更慢才对,所以唯一的原因应该是方法调用本身的损耗。
pojo put non volatile: 68  
unsafe put volatile: 114   // volatile属性,unsafe要快近6倍。
pojo put volatile: 795

  对于字符串操作,上述结论亦成立,所有的JUC包中的原子类AtomicXXX都是通过Unsafe实现的,其底层的各compareAndSwapXXX方法都是JNI实现。

  除了get/put外,Unsafe类还包含很多CAS(cas作为一种优化方式,其代价是cpu利用率在冲突高的时候很厉害,此时锁更合理)功能(JUC很多原子和CAS就是基于Unsafe实现)、park、unpark功能,LockSupport工具类就是调用的Unsafe的park与unpark实现的。

  注:Reflection.getCallerClass(n)已经被废弃,不应该再使用,它主要用来跟踪哪些类调用了本方法,不过Reflection.getCallerClass()仍然可用,在跟踪工具类的时候时还是比较有用的。

  关于Unsafe更全面的使用介绍,可以参见https://my.oschina.net/editorial-story/blog/3019773。

java MVEL2/Spring EL表达式、直接调用、反射性能实测

 
复制代码
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import org.mvel2.MVEL;

/**
 * Id 模型*/
public class Id implements Serializable {

    private Long id;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public static Long get(Id id) {
        return id == null ? null : id.getId();
    }

    public static void main(String[] args) {
        Id id = new Id();
        long beg = System.currentTimeMillis();
        for (long i=0;i<100000;i++) {
            id.setId(i);
        }
        System.out.println(System.currentTimeMillis() - beg);
        
        Map<String, Object> paramMap = new HashMap<>();
        String expression = "id.setId(1L);";
        paramMap.put("id", id);
        beg = System.currentTimeMillis();
        Serializable compiled =MVEL.compileExpression(expression);
        for (long i=0;i<100000;i++) {
            MVEL.eval(expression, paramMap);  // 非编译模式
//            MVEL.executeExpression(compiled,paramMap); // 编译模式
        }
        System.out.println(System.currentTimeMillis() - beg);
        beg = System.currentTimeMillis();
        try {
            Field field = Id.class.getDeclaredField("id");
            for (long i=0;i<100000;i++) {
                field.set(id, i);
            }
            System.out.println(System.currentTimeMillis() - beg);
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
}
复制代码

输出如下:

6  --原生调用
498   --MVEL2表达式

239 -- MVEL2 编译后
18   --反射

还差一个基于Unsafe直接访问的,后面补上。。。。

可见用表达式的性能是非常低下的,即使是编译后。如果真的什么时候需要用表达式的话,可以采用动态编译java类的方式实现。(它可以通过调用javac实现参考https://www.cnblogs.com/anai/p/4269858.html,也可以调用JavaCompiler,参考https://www.cnblogs.com/jxrichar/p/4883465.html)这样可以同时达到原生调用和灵活性的目标。

根据一些文章的性能评测,对于表达式语言,性能最好的是groovy、其次是MVEL,但是我们实际测下来,简单的语句grovvy的性能远不如mvel2。

网上还有一些OGNL & MVEL的性能测试比较(如https://caoyaojun1988-163-com.iteye.com/blog/2089726,还是比较客观的),实际上到现在2019年,我们会发现最终差别就在于有没有采用javac机制,没有采用javac机制的性能就是差一大截,否则不会相差太多,如果某个库说其性能特别NB如Fel,要么就是特定场景高度定制、要么author就是无脑,如果真有这样两全的方案,该方案要么被作为机密、要么被广泛open source了,不需要打广告。PS:我们在原来在一个稽核系统中采用mvel2对数以百万计的数据进行表达式计算,但是实际性能测试下来发现比原生java代码慢了20多倍,开发拒绝接收,最后我们硬是采用了javac的机制实现该特性,性能和原生java基本上相差不到10%。

当然,作为通用的表达式语言,MVEL2在性能要求不是很苛刻的场景中还是推荐使用的http://simpleframework.net/news/view?newsId=028c6068df804c548668b96db31a912b。

https://www.cnblogs.com/keithmo/p/5186693.html

https://www.techug.com/post/dynamic-code-in-java.html

https://blog.csdn.net/sunnyyoona/article/details/75244442

https://yanguz123.iteye.com/blog/2146176

https://www.iteye.com/topic/361794

https://blog.csdn.net/fhm727/article/details/6543152

http://simpleframework.net/news/view?newsId=028c6068df804c548668b96db31a912b

 

posted @ 2021-11-10 09:50  CharyGao  阅读(17)  评论(0)    收藏  举报