ReflectASM-invoke,高效率java反射机制原理
时间:2022-05-06
本文章向大家介绍ReflectASM-invoke,高效率java反射机制原理,主要内容包括一、性能对比、二、reflectasm原理解析。、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
前言:前段时间在设计公司基于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: // ' '
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());
}
}
由代码可以看出来,实际上ReflectASM就是把类的各个方法缓存起来,然后通过case选择,直接调用,因此速度会快上很多。但是它的get方法同样会消耗很大的时间,因此就算是使用ReflectASM的朋友也记得请在启动的时候就初始化get方法计入缓存。
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helvetica Neue"; color: #454545 }
- ArcGIS for Android学习(一)
- Arcgis for Silverlight学习(一)
- ImageButton与Button
- RadioButton、CheckBox与ToggleButton
- Silverlight学习(五)图形标绘
- radio与checkbox
- JavaScript之cookie
- Silverlight学习(四) domainservice动态多条件查询
- Android学习Tabhost、gallery、listview、imageswitcher
- ProgressBar、RatingBar和Spinner控件
- TimePicker控件、帧动画、补间动画
- Android学习之简单的数据存储
- Android学习之菜单
- Android简单登录系统
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Linux在丢失的情况下重置密码的教程
- Linux内核宏container_of的深度剖析
- CentOS 7.3配置Nginx虚拟主机的方法步骤
- Linux下alias命令的用法详解
- Linux搭建自己Nexus私服的实现方法
- centos7系统nginx服务器下phalcon环境搭建方法详解
- Logrotate实现Catalina.out日志每俩小时切割示例
- Linux常见问题解决方案汇总
- Linux中文件/文件夹无法删除的解决方案
- GNU Parallel的具体使用
- crontab定时任务不执行的原因分析与解决方法
- 在Linux中安装ASPNET.Core3.0运行时的示例代码
- linux Bash脚本判别使用者的身份方法示例
- 配置Linux使用LDAP用户认证的方法
- linux Dig命令使用大全