Java 反射慢?它到底慢在哪?

往期热门文章:

1、GitHub 被超火的 ChatGPT 霸榜!
2、Java使用 try catch会影响性能?
3、原来count(*)是接口性能差的真凶!
4、大公司病了,这也太形象了吧!!!
5、全球最大资源站创始人被抓,但网站还会继续活下去

反射具体是怎么影响性能的?这引起了我的反思。是啊,在阐述某个观点时确实有必要说明原因,并且证明这个观点是对的,虽然反射影响性能人尽皆知,我曾经也真的研究过反射是否存在性能问题,但并没有在写文章的时候详细说明。

这让我想到网上很多信息只会告诉你结论,并不会说明原因,导致很多学到的东西都是死记硬背,而不是真正掌握,别人一问或者自己亲身遇到同样的问题时,傻眼了。

反射真的存在性能问题吗?

为了放大问题,找到共性,采用逐渐扩大测试次数、每次测试多次取平均值的方式,针对同一个方法分别就直接调用该方法、反射调用该方法、直接调用该方法对应的实例、反射调用该方法对应的实例分别从1-1000000,每隔一个数量级测试一次:

测试代码如下(Person、ICompany、ProgramMonkey这三个类已在之前的文章中贴出):

public class ReflectionPerformanceActivity extends Activity{  private TextView mExecuteResultTxtView = null;  private EditText mExecuteCountEditTxt = null;  private Executor mPerformanceExecutor = Executors.newSingleThreadExecutor();  private static final int AVERAGE_COUNT = 10;  @Override  protected void onCreate(Bundle savedInstanceState){  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_reflection_performance_layout);  mExecuteResultTxtView = (TextView)findViewById(R.id.executeResultTxtId);  mExecuteCountEditTxt = (EditText)findViewById(R.id.executeCountEditTxtId);  }  public void onClick(View v){  switch(v.getId()){  case R.id.executeBtnId:{  execute();  }  break;  default:{  }  break;  }  }  private void execute(){  mExecuteResultTxtView.setText("");  mPerformanceExecutor.execute(new Runnable(){  @Override  public void run(){  long costTime = 0;  int executeCount = Integer.parseInt(mExecuteCountEditTxt.getText().toString());  long reflectMethodCostTime=0,normalMethodCostTime=0,reflectFieldCostTime=0,normalFieldCostTime=0;  updateResultTextView(executeCount + "毫秒耗时情况测试");  for(int index = 0; index < AVERAGE_COUNT; index++){  updateResultTextView("第 " + (index+1) + " 次");  costTime = getNormalCallCostTime(executeCount);  reflectMethodCostTime += costTime;  updateResultTextView("执行直接调用方法耗时:" + costTime + " 毫秒");  costTime = getReflectCallMethodCostTime(executeCount);  normalMethodCostTime += costTime;  updateResultTextView("执行反射调用方法耗时:" + costTime + " 毫秒");  costTime = getNormalFieldCostTime(executeCount);  reflectFieldCostTime += costTime;  updateResultTextView("执行普通调用实例耗时:" + costTime + " 毫秒");  costTime = getReflectCallFieldCostTime(executeCount);  normalFieldCostTime += costTime;  updateResultTextView("执行反射调用实例耗时:" + costTime + " 毫秒");  }  updateResultTextView("执行直接调用方法平均耗时:" + reflectMethodCostTime/AVERAGE_COUNT + " 毫秒");  updateResultTextView("执行反射调用方法平均耗时:" + normalMethodCostTime/AVERAGE_COUNT + " 毫秒");  updateResultTextView("执行普通调用实例平均耗时:" + reflectFieldCostTime/AVERAGE_COUNT + " 毫秒");  updateResultTextView("执行反射调用实例平均耗时:" + normalFieldCostTime/AVERAGE_COUNT + " 毫秒");  }  });  }  private long getReflectCallMethodCostTime(int count){  long startTime = System.currentTimeMillis();  for(int index = 0 ; index < count; index++){  ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);  try{  Method setmLanguageMethod = programMonkey.getClass().getMethod("setmLanguage", String.class);  setmLanguageMethod.setAccessible(true);  setmLanguageMethod.invoke(programMonkey, "Java");  }catch(IllegalAccessException e){  e.printStackTrace();  }catch(InvocationTargetException e){  e.printStackTrace();  }catch(NoSuchMethodException e){  e.printStackTrace();  }  }  return System.currentTimeMillis()-startTime;  }  private long getReflectCallFieldCostTime(int count){  long startTime = System.currentTimeMillis();  for(int index = 0 ; index < count; index++){  ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);  try{  Field ageField = programMonkey.getClass().getDeclaredField("mLanguage");  ageField.set(programMonkey, "Java");  }catch(NoSuchFieldException e){  e.printStackTrace();  }catch(IllegalAccessException e){  e.printStackTrace();  }  }  return System.currentTimeMillis()-startTime;  }  private long getNormalCallCostTime(int count){  long startTime = System.currentTimeMillis();  for(int index = 0 ; index < count; index++){  ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);  programMonkey.setmLanguage("Java");  }  return System.currentTimeMillis()-startTime;  }  private long getNormalFieldCostTime(int count){  long startTime = System.currentTimeMillis();  for(int index = 0 ; index < count; index++){  ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);  programMonkey.mLanguage = "Java";  }  return System.currentTimeMillis()-startTime;  }  private void updateResultTextView(final String content){  ReflectionPerformanceActivity.this.runOnUiThread(new Runnable(){  @Override  public void run(){  mExecuteResultTxtView.append(content);  mExecuteResultTxtView.append("\n");  }  });  }  
}

测试结果如下:

c0aeae85e9c7f0078accc3220fd0a152.jpeg

反射性能测试结果

测试结论:

  • 反射的确会导致性能问题;

  • 反射导致的性能问题是否严重跟使用的次数有关系,如果控制在100次以内,基本上没什么差别,如果调用次数超过了100次,性能差异会很明显;

  • 四种访问方式,直接访问实例的方式效率最高;其次是直接调用方法的方式,耗时约为直接调用实例的1.4倍;接着是通过反射访问实例的方式,耗时约为直接访问实例的3.75倍;最慢的是通过反射访问方法的方式,耗时约为直接访问实例的6.2倍;

反射到底慢在哪?

跟踪源码可以发现,四个方法中都存在实例化ProgramMonkey的代码,所以可以排除是这句话导致的不同调用方式产生的性能差异;通过反射调用方法中调用了setAccessible方法,但该方法纯粹只是设置属性值,不会产生明显的性能差异;所以最有可能产生性能差异的只有getMethod和getDeclaredField、invoke和set方法了,下面分别就这两组方法进行测试,找到具体慢在哪?

首先测试invoke和set方法,修改getReflectCallMethodCostTime和getReflectCallFieldCostTime方法的代码如下:

private long getReflectCallMethodCostTime(int count){  long startTime = System.currentTimeMillis();  ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);  Method setmLanguageMethod = null;  try{  setmLanguageMethod = programMonkey.getClass().getMethod("setmLanguage", String.class);  setmLanguageMethod.setAccessible(true);  }catch(NoSuchMethodException e){  e.printStackTrace();  }  for(int index = 0 ; index < count; index++){  try{  setmLanguageMethod.invoke(programMonkey, "Java");  }catch(IllegalAccessException e){  e.printStackTrace();  }catch(InvocationTargetException e){  e.printStackTrace();  }  }  return System.currentTimeMillis()-startTime;  }  private long getReflectCallFieldCostTime(int count){  long startTime = System.currentTimeMillis();  ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);  Field ageField = null;  try{  ageField = programMonkey.getClass().getDeclaredField("mLanguage");  }catch(NoSuchFieldException e){  e.printStackTrace();  }  for(int index = 0 ; index < count; index++){  try{  ageField.set(programMonkey, "Java");  }catch(IllegalAccessException e){  e.printStackTrace();  }  }  return System.currentTimeMillis()-startTime;  }

沿用上面的测试方法,测试结果如下:

abf4cdc97f90be2d9cad84e5b693231a.jpeg

invoke和set

修改getReflectCallMethodCostTime和getReflectCallFieldCostTime方法的代码如下,对getMethod和getDeclaredField进行测试

private long getReflectCallMethodCostTime(int count){  long startTime = System.currentTimeMillis();  ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);  for(int index = 0 ; index < count; index++){  try{  Method setmLanguageMethod = programMonkey.getClass().getMethod("setmLanguage", String.class);  }catch(NoSuchMethodException e){  e.printStackTrace();  }  }  return System.currentTimeMillis()-startTime;  
}  private long getReflectCallFieldCostTime(int count){  long startTime = System.currentTimeMillis();  ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);  for(int index = 0 ; index < count; index++){  try{  Field ageField = programMonkey.getClass().getDeclaredField("mLanguage");  }catch(NoSuchFieldException e){  e.printStackTrace();  }  }  return System.currentTimeMillis()-startTime;  
}

沿用上面的测试方法,测试结果如下:

801242fbf47519547b5e7d31841ca51e.jpeg

getMethod和getDeclaredField

测试结论:

  • getMethod和getDeclaredField方法会比invoke和set方法耗时;

  • 随着测试数量级越大,性能差异的比例越趋于稳定;

由于测试的这四个方法最终调用的都是native方法,无法进一步跟踪。个人猜测应该是和在程序运行时操作class有关,比如需要判断是否安全?是否允许这样操作?入参是否正确?是否能够在虚拟机中找到需要反射的类?另外,最新最全的 Java 面试题整理好了,点击Java面试库小程序在线刷题。

主要是这一系列判断条件导致了反射耗时;也有可能是因为调用natvie方法,需要使用JNI接口,导致了性能问题(参照Log.java、System.out.println,都是调用native方法,重复调用多次耗时很明显)。

如果避免反射导致的性能问题?


通过上面的测试可以看出,过多地使用反射,的确会存在性能问题,但如果使用得当,所谓反射导致性能问题也就不是问题了,关于反射对性能的影响,参照下面的使用原则,并不会有什么明显的问题:

  • 不要过于频繁地使用反射,大量地使用反射会带来性能问题;

  • 通过反射直接访问实例会比访问方法快很多,所以应该优先采用访问实例的方式。

后记

上面的测试并不全面,但在一定程度上能够反映出反射的确会导致性能问题,也能够大概知道是哪个地方导致的问题。如果后面有必要进一步测试,我会从下面几个方面作进一步测试:

  • 测试频繁调用native方法是否会有明显的性能问题;

  • 测试同一个方法内,过多的条件判断是否会有明显的性能问题;

  • 测试类的复杂程度是否会对反射的性能有明显影响。

 

作者:张明云
来源:https://www.jianshu.com/p/4e2b49fa8ba1

往期热门文章:
1、工作十年,入职新公司不到四个月就遇到裁员
2、SpringBoot 设置动态定时任务,千万别再写死了~
3、一款SQL自动检查神器,再也不用担心SQL出错了!
4、重磅:国产IDE发布,由阿里研发,完全开源!(高性能+高定制性)
5、马斯克收购 Twitter:把你们的代码都打印出来
6、互联网最值得加入的173家国企汇总
7、国家电网还是拼多多,选哪个?
8、我写代码equals关键字没用好,线上系统被我捅出一个生产事故!
9、我是真没想到,这个面试题居然从11年前就开始讨论了,而官方今年才表态。
10、腾讯员工痛诉从阿里来的同事秀内卷。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/45938.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

运营人必懂 | TikTok运营指南

“TikTok之前确实很火&#xff0c;现在呢&#xff1f;” 最新数据告诉你&#xff1a; Sensor Tower商店情报数据显示&#xff0c;2022年9月抖音及海外版TikTok在全球App Store和Google Play吸金超过3.15亿美元&#xff0c;是去年同期的1.7倍&#xff0c;蝉联全球移动应用&…

抖音号运营爆量爆单技巧

泛知识付费2.0时代&#xff0c;短视频、直播间成为了知识传播的重要阵地&#xff0c;只要有技能干货&#xff0c;不论是行业大咖&#xff0c;还是精通某领域的普通人&#xff0c;都有机会成为大众的“老师”&#xff0c;依靠输出视频、音频等内容课程来知识变现。而抖音生态&am…

抖音直播运营分析必备工具!千川投放提高付费流量转化ROI

千川正式上线后的几个月&#xff0c;由于操作更简单越来越多的商家已经通过千川把新号快速的跑起来了&#xff0c;不少新号已经实现高投产比。但还有很多新手商家&#xff0c;遇到新号起号难&#xff0c;千川投放没产出等问题&#xff0c;最近讨论比较多的主要有&#xff1a; …

抖音运营方针策略

抖音&#xff0c;是一款可以拍短视频的音乐创意短视频社交软件&#xff0c;该软件于2016年9月上线&#xff0c;是一个专注年轻人音乐短视频社区平台。用户可以通过这款软件选择歌曲&#xff0c;拍摄音乐短视频&#xff0c;形成自己的作品。 抖音运营是一款社交类的软件&#xf…

抖音账号矩阵系统如何开发布局及运营?

抖音SEO&#xff0c;顾名思义抖音搜索排名优化&#xff0c;目前短视频平台如火如荼&#xff0c;抖音也凭借自身优势&#xff0c;迅猛发展&#xff0c;据相关数据统计&#xff0c;目前抖音平台日活用户已超8亿&#xff0c;抖音搜索超4亿&#xff0c;商业价值巨大&#xff0c;这无…

抖音运营经验与抖音机制分享!

如今运营抖音号&#xff0c;一个月涨了10w粉丝&#xff0c;虽然不多&#xff0c;但也是很好的开始。其实&#xff0c;要玩抖音除了要懂基本的机制&#xff0c;避开雷区外&#xff0c;还有一些小窍门&#xff0c;下面武汉新时标文化传媒有限公司总结抖音经验与抖音机制&#xff…

问答ChatGPT-4:探索未来微数据中心IDC的发展趋势

从去年年底开始到现在&#xff0c;大众对以ChatGPT-4为主的人工智能AI的话题讨论盛况空前。这是一款由OpenAI发布的聊天机器人模型&#xff0c;一经上线&#xff0c;短短5天完成100万用户积累&#xff0c;并在最近实现月活用户破亿。实际上&#xff0c;ChatGPT和智能客服、智能…

人工智能工具集合

前言 ​ 话说2023&#xff0c;可谓人工智能元年&#xff01;但是说实话&#xff0c;有部分产品达不到“人工智能”的程度&#xff0c;还停留在“计算”的层次&#xff0c;我对这方面不是专家&#xff0c;就不去评价这些应用是否算是“AI”了。不过&#xff0c;善用这些工具&am…

巴比特 | 元宇宙每日必读:28家AI企业集体赴港IPO,业绩惨谈,经营亏损,这是AI下半场的起点,还是资本炒作到顶峰的标志?...

摘要&#xff1a;当前&#xff0c;港交所通过聆讯等待IPO的排队企业一共有106家&#xff0c;其中&#xff0c;涉及AI人工智能或数字化的公司达到28家&#xff0c;占比26%。而这28家公司中&#xff0c;有18家截至2022年度的净利润为亏损&#xff0c;8家实现微盈利。 业绩惨淡&am…

AI绘画最全提示词表;独立开发者2项重要能力;AIGC发展「百科全书」;AI新时代的学习/创业/出海 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 3300提示词&#xff0c;AI作画艺术家/风格/细节的最全词表 这是AI绘画工具「画宇亩 (Nolibox Creator) 」帮助中心的一份学习文档&…

input框出现转圈问题

input框不能加type 只有select框可以加type 若想使用数字输入框,可以用InputNumber或正则表达式验证

计算机重启后一直黑屏转圈,电脑win10开机黑屏转圈十几秒怎么解决

对于Win10正式版开机等待时间长的问题&#xff0c;通常只发生在通过“升级”方式更新的Win10正式版系统中。而对于采用“全新”方式安装 的Win10正式版系统中则不会出现此类故障&#xff0c;下面一起来看看解决方法吧。 1、右击桌面左下角的“Windows”按钮&#xff0c;从其右键…

计算机重启后一直黑屏转圈,技术员教你解决win10开机无限黑屏转圈重启的解决方法...

大家在操作电脑时一定会遇到很多问题&#xff0c;比如说win10开机无限黑屏转圈重启的解决方法就是我们经常会遇到的&#xff0c;小编及身边的朋友也遇到过win10开机无限黑屏转圈重启的解决方法很多次&#xff0c;针对win10开机无限黑屏转圈重启的解决方法这样的问题&#xff0c…

html怎样让图片自动转圈,html5如何实现图片转圈的动画效果

这篇文章主要介绍了html5实现图片转圈的动画效果——让页面动起来的相关资料,需要的朋友可以参考下 1.先瞧瞧效果&#xff1a; 2.代码是这样的&#xff1a; mixin ani-btnRotate{ keyframes btnRotate{ from{transform: rotateZ(0);} to{transform: rotateZ(360deg);} } } incl…

右键文件一直转圈卡死

右键文件一直转圈卡死 1.随便打开一个电脑管理器&#xff08;比如&#xff0c;360&#xff09;&#xff1b; 2.点击搜索更多&#xff0c;搜索“右键管理”&#xff1b; 3.点开后&#xff0c;将其中不需要的右键管理删除&#xff08;如果需要恢复&#xff0c;可以点击已删除…

解决win系统点击右键时鼠标一直不停转圈操作

WinR输入regedit打开注册表编辑器&#xff0c;在HKEY_CLASSES_ROOT目录下的\Directory下找到如下图所示的文件&#xff1a; 删除除了New以外的所有其他文件即可。 如不小心删除了New文件&#xff0c;不要慌张&#xff0c;可邮件ContextMenuHandlers选择新建“项”再新建New…

Postman打开一直转圈

打开postman一直转圈圈&#xff0c;网上找的都是不解决问题。后来发现个大神完美的解决了&#xff0c;具体步骤如下&#xff1a; 1 解决方案&#xff1a;删除%appdata%目录下的postman文件。这个文件夹是隐藏的&#xff0c;建议先在地址栏输入%appdata%进入此目录 2 操作完第…

计算机开机桌面一直转圈,Win10开机一直转圈圈怎么办?一直转圈进不去系统的解决方法...

最近遇到一个问题&#xff0c;这算是比较常见的故障了&#xff0c;那就是电脑开机一直转圈圈&#xff0c;等了好久也不能进入Win10系统的桌面中&#xff0c;可能大家遇到这个问题会直接重装系统来解决&#xff0c;但是重装系统很麻烦&#xff0c;不妨可以尝试一个方法&#xff…

C++实现转圈打印矩阵

给定一个整型矩阵 matrix&#xff0c;请按照转圈的方式打印它。 1. 题目描述 例如&#xff1a;    1  2  3  4    5  6  7  8    9   10   11  12    13  14  15  16 打印结果为&#xff1a;1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10 要求额外空间复…

转圈加载html,web前端入门到实战:纯CSS实现加载转圈样式

web前端入门到实战:纯CSS实现加载转圈样式 发布时间:2020-05-27 18:11:33 来源:51CTO 阅读:134 作者:前端向南 不同的项目中对于等待加载时转圈圈的样式是不同的,有的是传统的转圈的gif图片,见得比较多的是将转圈圈的换成了可爱的图标。有时候项目中加入等待加载的图片会…