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

每种方式执行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: // '\0'  
            return user.getName();  
  
        case 1: // '\001'  
            return Integer.valueOf(user.getId());  
  
        case 2: // '\002'  
            user.setName((String)aobj[0]);  
            return null;  
  
        case 3: // '\003'  
            user.setId(((Integer)aobj[0]).intValue());  
            return null;  
        }  
        throw new IllegalArgumentException((new StringBuilder("Method not found: ")).append(i).toString());  
    }  
}  

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

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helvetica Neue"; color: #454545 }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏非著名程序员

Java 反射基础(上)

? 原文链接: http://blog.csdn.net/My_TrueLove/article/details/51298218 写在前面: 投稿作者是一位...

1969
来自专栏C/C++基础

C++IO流简介

输入输出(IO)是指计算机同任何外部设备之间的数据传递。常见的输入输出设备有文件、键盘、打印机、屏幕等。数据可以按记录(或称数据块)的方式传递,也可以 流的方式...

1253
来自专栏程序员八阿哥

王老板Python面试(6):25道Python工程师面试必备知识点!

Python是一种解释型语言。这就是说,与C语言和C的衍生语言不同,Python代码在运行之前不需要编译。其他解释型语言还包括PHP和Ruby。

661
来自专栏决胜机器学习

设计模式专题(六)——原型模式

设计模式专题(六) ——原型模式 (原创内容,转载请注明来源,谢谢) 一、含义 原型模式(Prototype)是用原型实例指定创建对象的种类,并且通过拷贝这些...

3428
来自专栏Java技术栈

涨姿势 | 优秀 Java 程序员写代码的风格

1864
来自专栏生信宝典

Python学习极简教程 (一)

Python 教程 欢迎来到Python的世界,本教程将带你遨游Python,领悟Python的魅力。本教程专注于帮助初学者,尤其是生物信息分析人员快速学会P...

44311
来自专栏微信公众号:Java团长

优秀Java程序员的编程风格

今天突发奇想,对编码习惯和编程风格很感兴趣,于是乎,找了一下关于编程风格(Java篇)的资料,希望对爱好编码或者开始学习编码的同学有帮助!

702
来自专栏码云1024

c++ Struct和Class的区别

2443
来自专栏C/C++基础

C++中mutable关键字的用法

mutalbe的中文意思是“可变的,易变的”,是constant(即C++中的const)的反义词。在C++中,mutable也是为了突破const的限制而设置...

581
来自专栏大内老A

深入理解string和如何高效地使用string

无论你所使用的是哪种编程语言,我们都不得不承认这样一个共识:string是我们使用最为频繁的一种对象。但是string的常用性并不意味着它的简单性,而且我认为,...

24010

扫码关注云+社区