zoukankan      html  css  js  c++  java
  • ReflectASM-invoke,高效率java反射机制原理

    前言:前段时间在设计公司基于netty的易用框架时,很多地方都用到了反射机制。反射的性能一直是大家有目共睹的诟病,相比于直接调用速度上差了很多。但是在很多地方,作为未知通用判断的时候,不得不调用反射类型来保障代码的复用性和框架的扩展性。所以我们只能想办法优化反射,而不能抵制反射,那么优化方案,这里给大家推荐了ReflectASM。

    一、性能对比

    我们先通过简单的代码来看看,各种调用方式之间的性能差距。

    public static void main(String[] args) throws Exception {
            ApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"spring-common.xml"});    
            new InitMothods().initApplicationContext(ac);
            
            
            long now;
            HttpRouteClassAndMethod route = InitMothods.getTaskHandler("GET:/login/getSession");
            Map map = new HashMap();
            
            //-----------------------最粗暴的直接调用
            
            now = System.currentTimeMillis();
             
            for(int i = 0; i<5000000; ++i){
                new LoginController().getSession(map);
            }
            System.out.println("get耗时"+(System.currentTimeMillis() - now) + "ms);
            
            
            //---------------------常规的invoke
            
            now = System.currentTimeMillis();
             
            for(int i = 0; i<5000000; ++i){
                Class<?> c = Class.forName("com.business.controller.LoginController");
               
                Method m = c.getMethod("getSession",Map.class);
                m.invoke(SpringApplicationContextHolder.getSpringBeanForClass(route.getClazz()), map);
            }
            System.out.println("标准反射耗时"+(System.currentTimeMillis() - now) + "ms);
            
             
            //---------------------缓存class的invoke
            
            now = System.currentTimeMillis();
             
            for(int i = 0; i<5000000; ++i){
                try {
                    route.getMethod().invoke(SpringApplicationContextHolder.getSpringBeanForClass(route.getClazz()),
                            new Object[]{map});
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
             
            System.out.println("缓存反射耗时"+(System.currentTimeMillis() - now) + "ms秒);
            
            
            //---------------------reflectasm的invoke
            
            MethodAccess ma = MethodAccess.get(route.getClazz());
            int index = ma.getIndex("getSession");
            now = System.currentTimeMillis();
             
            for(int i = 0; i<5000000; ++i){
                ma.invoke(SpringApplicationContextHolder.getSpringBeanForClass(route.getClazz()), index, map);
            }
            System.out.println("reflectasm反射耗时"+(System.currentTimeMillis() - now) + "ms);
     
        }
    View Code

    每种方式执行500W次运行结果如下:

    • get耗时21ms
    • 标准反射耗时5397ms
    • 缓存反射耗时315ms
    • reflectasm反射耗时275ms

    (时间长度请忽略,因为每个人的代码业务不一致,主要看体现的差距,多次运行效果基本一致。)

    结论:方法直接调用属于最快的方法,其次是java最基本的反射,而反射中又分是否缓存class两种,由结果得出其实反射中很大一部分时间是在查找class,实际invoke效率还是不错的。而reflectasm反射效率要在java传统的反射之上快了接近1/3.

    二、reflectasm原理解析。

    ReflectASM是一个很小的java类库,主要是通过asm生产类来实现java反射。他的主要代码还是get方法,但是由于get方法源码比较多,就不在博客中贴出来了,大家可以自己点进去看。这里我们给出实现invoke的抽象方法。

    // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.  
    // Jad home page: http://www.kpdus.com/jad.html  
    // Decompiler options: packimports(3)   
      
    package com.johhny.ra;  
      
    import com.esotericsoftware.reflectasm.MethodAccess;  
      
    // Referenced classes of package com.johhny.ra:  
    //            User  
      
    public class UserMethodAccess extends MethodAccess  
    {  
      
        public UserMethodAccess()  
        {  
        }  
      
        /** 
         * 这个方法是主要是实现了MethodAccess 的抽象方法,来实现反射的功能   
         * @param obj  需要反射的对象 
         * @param i  class.getDeclaredMethods 对应方法的index 
         * @param 参数对象集合 
         * @return 
         */  
        public transient Object invoke(Object obj, int i, Object aobj[])  
        {  
            User user = (User)obj;  
            switch(i)  
            {  
            case 0: // ''  
                return user.getName();  
      
            case 1: // '01'  
                return Integer.valueOf(user.getId());  
      
            case 2: // '02'  
                user.setName((String)aobj[0]);  
                return null;  
      
            case 3: // '03'  
                user.setId(((Integer)aobj[0]).intValue());  
                return null;  
            }  
            throw new IllegalArgumentException((new StringBuilder("Method not found: ")).append(i).toString());  
        }  
    }  
    View Code

    由代码可以看出来,实际上ReflectASM就是把类的各个方法缓存起来,然后通过case选择,直接调用,因此速度会快上很多。但是它的get方法同样会消耗很大的时间,因此就算是使用ReflectASM的朋友也记得请在启动的时候就初始化get方法计入缓存。

     

  • 相关阅读:
    es6 --- var const let
    HTTP -- 请求/响应 结构
    点击下载文件
    计算机当前时间
    js -- img 随着鼠标滚轮的变化变化
    vue --- 全局守卫
    vue -- 路由懒加载
    vuex -- 状态管理
    js对数组进行操作
    高性能网站建设
  • 原文地址:https://www.cnblogs.com/tohxyblog/p/8661090.html
Copyright © 2011-2022 走看看