详解提高使用Java反射的效率方法


Posted in PHP onApril 29, 2019

在我们平时的工作或者面试中,都会经常遇到“反射”这个知识点,通过“反射”我们可以动态的获取到对象的信息以及灵活的调用对象方法等,但是在使用的同时又伴随着另一种声音的出现,那就是“反射”很慢,要少用。难道反射真的很慢?那跟我们平时正常创建对象调用方法比慢多少? 估计很多人都没去测试过,只是”道听途说“。下面我们就直接通过一些测试用例来直观的感受一下”反射“。
正文

准备测试对象

下面先定义一个测试的类TestUser,只有id跟name属性,以及它们的getter/setter方法,另外还有一个自定义的sayHi方法。

public class TestUser { private Integer id; private String name; 

 public String sayHi(){  return "hi";

 } public Integer getId() {  return id;

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

 } public String getName() {  return name;

 } public void setName(String name) {  this.name = name;

 }

}

测试创建100万个对象

// 通过普通方式创建TestUser对象@Testpublic void testCommon(){ long start = System.currentTimeMillis();

 TestUser user = null; int i = 0; while(i<1000000){

  ++i;

  user = new TestUser();

 } long end = System.currentTimeMillis();

 System.out.println("普通对象创建耗时:"+(end - start ) + "ms");

}//普通对象创建耗时:10ms
// 通过反射方式创建TestUser对象@Testpublic void testReflexNoCache() throws Exception { long start = System.currentTimeMillis();

 TestUser user = null; int i = 0; while(i<1000000){

  ++i;

  user = (TestUser) Class.forName("ReflexDemo.TestUser").newInstance();

 } long end = System.currentTimeMillis();

 System.out.println("无缓存反射创建对象耗时:"+(end - start ) + "ms");

}//无缓存反射创建对象耗时:926ms

在上面这两个测试方法中,笔者各自测了5次,把他们消耗的时间取了一个平均值,在输出结果中可以看到一个是10ms,一个是926ms,在创建100W个对象的情况下,反射居然慢了90倍左右。wtf?差距居然这么大?难道反射真的这么慢?下面笔者换一种反射的姿势,继续测试一下,看看结果如何

// 通过缓存反射方式创建TestUser对象@Testpublic void testReflexWithCache() throws Exception { long start = System.currentTimeMillis();

 TestUser user = null;

 Class rUserClass = Class.forName("RefleDemo.TestUser"); int i = 0; while(i<1000000){

  ++i;

  user = (TestUser) rUserClass.newInstance();

 } long end = System.currentTimeMillis();

 System.out.println("通过缓存反射创建对象耗时:"+(end - start ) + "ms");

}//通过缓存反射创建对象耗时:41ms

其实通过代码我们可以发现,是Class.forName这个方法比较耗时,它实际上调用了一个本地方法,通过这个方法来要求JVM查找并加载指定的类。所以我们在项目中使用的时候,可以把Class.forName返回的Class对象缓存起来,下一次使用的时候直接从缓存里面获取,这样就极大的提高了获取Class的效率。同理,在我们获取Constructor、Method等对象的时候也可以缓存起来使用,避免每次使用时再来耗费时间创建。

测试反射调用方法

@Testpublic void testReflexMethod() throws Exception { long start = System.currentTimeMillis();

 Class testUserClass = Class.forName("RefleDemo.TestUser");

 TestUser testUser = (TestUser) testUserClass.newInstance();

 Method method = testUserClass.getMethod("sayHi"); int i = 0; while(i<100000000){

  ++i;

  method.invoke(testUser);

 } long end = System.currentTimeMillis();

 System.out.println("反射调用方法耗时:"+(end - start ) + "ms");

}//反射调用方法耗时:330ms
@Testpublic void testReflexMethod() throws Exception { long start = System.currentTimeMillis();

 Class testUserClass = Class.forName("RefleDemo.TestUser");

 TestUser testUser = (TestUser) testUserClass.newInstance();

 Method method = testUserClass.getMethod("sayHi"); int i = 0; while(i<100000000){

  ++i;

  method.setAccessible(true);

  method.invoke(testUser);

 } long end = System.currentTimeMillis();

 System.out.println("setAccessible=true 反射调用方法耗时:"+(end - start ) + "ms");

}//setAccessible=true 反射调用方法耗时:188ms

这里我们反射调用sayHi方法1亿次,在调用了method.setAccessible(true)后,发现快了将近一半。查看API可以了解到,jdk在设置获取字段,调用方法的时候会执行安全访问检查,而此类操作会比较耗时,所以通过setAccessible(true)的方式可以关闭安全检查,从而提升反射效率。

极致的反射

除了上面的手段,还有没有什么办法可以更极致的使用反射呢?这里介绍一个高性能反射工具包ReflectASM。它是通过字节码生成的方式来实现的反射机制,下面是一个跟java反射的性能比较。

详解提高使用Java反射的效率方法

结语

最后总结一下,为了更好的使用反射,我们应该在项目启动的时候将反射所需要的相关配置及数据加载进内存中,在运行阶段都从缓存中取这些元数据进行反射操作。大家也不用惧怕反射,虚拟机在不断的优化,只要我们方法用的对,它并没有”传闻“中的那么慢,当我们对性能有极致追求的时候,可以考虑通过三方包,直接对字节码进行操作。

PHP 相关文章推荐
让你的WINDOWS同时支持MYSQL4,MYSQL4.1,MYSQL5X
Dec 06 PHP
用php获取远程图片并把它保存到本地的代码
Apr 07 PHP
比较时间段一与时间段二是否有交集的php函数
May 31 PHP
php header Content-Type类型小结
Jul 03 PHP
CURL的学习和应用(附多线程实现)
Jun 03 PHP
PHP+jquery实时显示网站在线人数的方法
Jan 04 PHP
PHP实现连接设备、通讯和发送命令的方法
Oct 13 PHP
php用正则判断是否为数字的方法
Mar 25 PHP
php 判断字符串编码是utf-8 或gb2312实例
Nov 01 PHP
PHP实现的Redis多库选择功能单例类
Jul 27 PHP
PHP实现的pdo连接数据库并插入数据功能简单示例
Mar 30 PHP
PHP实现爬虫爬取图片代码实例
Mar 03 PHP
Thinkphp整合阿里云OSS图片上传实例代码
Apr 28 #PHP
详解php中生成标准uuid(guid)的方法
Apr 28 #PHP
PHP写API输出的时用echo的原因详解
Apr 28 #PHP
PHP+mysql防止SQL注入的方法小结
Apr 27 #PHP
PHP常用工具函数小结【移除XSS攻击、UTF8与GBK编码转换等】
Apr 27 #PHP
PHP操作路由器实现方法示例
Apr 27 #PHP
PHP切割汉字的常用方法实例总结
Apr 27 #PHP
You might like
投票管理程序
2006/10/09 PHP
深入分析使用mysql_fetch_object()以对象的形式返回查询结果
2013/06/05 PHP
基于PHP常用字符串的总结(待续)
2013/06/07 PHP
PHP判断远程图片是否存在的几种方法
2014/05/04 PHP
PHP实现GIF图片验证码
2015/11/04 PHP
phpStudy访问速度慢和启动失败的解决办法
2015/11/19 PHP
thinkphp框架使用JWTtoken的方法详解
2019/10/10 PHP
HTML5如何适配 iPhone IOS 底部黑条
2021/03/09 HTML / CSS
Script标签与访问HTML页面详解
2014/01/10 Javascript
js中精确计算加法和减法示例
2014/03/28 Javascript
JavaScript函数定义的常见注意事项小结
2014/09/16 Javascript
Vue.js每天必学之计算属性computed与$watch
2016/09/05 Javascript
javascript 注释代码的几种方法总结
2017/01/04 Javascript
微信小程序 实例开发总结
2017/04/26 Javascript
Javascript实现一个简单的输入关键字添加标签效果实例
2017/06/01 Javascript
基于 Bootstrap Datetimepicker 联动
2017/08/03 Javascript
利用Angular7开发一个Radio组件的全过程
2019/07/11 Javascript
javascript实现简易计算器功能
2020/09/23 Javascript
[40:19]完美世界DOTA2联赛PWL S3 Rebirth vs CPG 第二场 12.18
2020/12/19 DOTA
python实现的二叉树算法和kmp算法实例
2014/04/25 Python
python好玩的项目—色情图片识别代码分享
2017/11/07 Python
给我一面国旗 python帮你实现
2019/09/30 Python
浅谈Tensorflow 动态双向RNN的输出问题
2020/01/20 Python
解决Keras使用GPU资源耗尽的问题
2020/06/22 Python
把Anaconda中的环境导入到Pycharm里面的方法步骤
2020/10/30 Python
纯HTML+CSS3制作导航菜单(附源码)
2013/04/24 HTML / CSS
StringBuilder和String的区别
2015/05/18 面试题
精神文明单位申报材料
2014/05/02 职场文书
优秀毕业生找工作自荐信
2014/06/23 职场文书
优秀会计求职信
2014/07/04 职场文书
入股协议书范本
2014/11/01 职场文书
2014年劳动部工作总结
2014/12/11 职场文书
致青春观后感
2015/06/09 职场文书
JS中一些高效的魔法运算符总结
2021/05/06 Javascript
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
2022/05/04 Python
微信小程序纯CSS实现无限弹幕滚动效果
2022/09/23 HTML / CSS