【注解和反射】性能分析

news2024/12/27 11:22:42

继上一篇博客【注解和反射】通过反射动态创建对象、调用普通方法、操作属性-CSDN博客

目录

九、性能分析

代码分析

完整代码

分析结果


九、性能分析

代码分析

(1)这部分代码是一个简单的基准测试,它测量了在Java中使用普通方式调用一个对象的getter方法(即getName())10亿次所需的时间。

public static void test01(){
    User user = new User();
    long starttime = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
      user.getName();
    }
    long endtime = System.currentTimeMillis();
    System.out.println("普通方式执行10亿次,需要时间:" + (endtime - starttime) + "ms");
  }

下面是代码的详细解释:

  1. User user = new User(); - 创建一个User对象实例,并赋值给变量user

  2. long starttime = System.currentTimeMillis(); - 获取当前的系统时间(以毫秒为单位),并赋值给变量starttime。这个时间将用作基准测试的起始时间。

  3. for (int i = 0; i < 1000000000; i++) { user.getName(); } - 一个循环,它将调用user.getName()方法10亿次。注意,这里只是调用了方法,但并没有使用它的返回值。这个循环的目的是为了对getName()方法进行大量的重复调用,以便能够测量其性能。

  4. long endtime = System.currentTimeMillis(); - 在循环执行完成后,再次获取当前的系统时间,并赋值给变量endtime。这个时间将用作基准测试的结束时间。

  5. System.out.println("普通方式执行10亿次,需要时间:" + (endtime - starttime) + "ms"); - 计算基准测试的执行时间(以毫秒为单位),并将其输出到控制台。这是通过从endtime中减去starttime来实现的。

总的来说,这段代码的目的是为了测量在Java中重复调用一个对象的getter方法(即getName())的性能。但是,值得注意的是,这个测试可能并不是非常准确或有用,因为它并没有考虑JVM的热点优化等因素。在实际的性能测试中,通常需要使用更复杂的工具和方法来确保结果的准确性和可靠性。此外,getName()方法的具体实现和User类的定义也会影响测试结果。如果getName()方法非常简单,那么这个测试可能主要是测量循环和方法调用的开销,而不是getName()方法本身的性能。

(2)这部分代码是另一个基准测试,与之前的test01方法类似,但它使用了Java的反射机制来调用User对象的getName方法。反射是Java的一个强大特性,它允许程序在运行时检查和修改类的行为。在这个测试中,反射被用来动态地调用方法,而不是像test01中那样直接调用。

public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();
    Method getName = c1.getMethod("getName", null);
    long starttime = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
      getName.invoke(user, null);
    }
    long endtime = System.currentTimeMillis();
    System.out.println("反射方式执行10亿次,需要时间:" + (endtime - starttime) + "ms");
  }

下面是代码的详细解释:

另外,请注意代码中可能存在的错误,特别是在使用getMethodinvoke方法时参数的处理。这些错误可能会导致NoSuchMethodExceptionIllegalArgumentException异常。

总的来说,这段代码的目的是为了测量在Java中使用反射机制重复调用一个对象的方法的性能。反射通常比直接调用方法要慢,因为它涉及到了额外的类型检查和动态分派。这个测试可以用来量化这种性能差异。然而,需要注意的是,这个测试可能受到JVM优化、方法内联和其他因素的影响,因此在实际应用中可能需要更复杂的基准测试来获取准确的结果。

  1. User user = new User(); - 创建一个User对象实例,并赋值给变量user

  2. Class c1 = user.getClass(); - 调用user对象的getClass方法来获取其运行时类的Class对象,并将其赋值给变量c1

  3. Method getName = c1.getMethod("getName", null); - 使用Class对象的getMethod方法来获取表示getName方法的Method对象。这里传递了方法名"getName"和一个null参数,表示该方法不接受任何参数。但是,这里有一个错误:getMethod的第二个参数应该是一个Class<?>... parameterTypes的可变参数数组,用来指定方法的参数类型。如果getName方法没有参数,应该传递一个空数组而不是null。正确的调用应该是c1.getMethod("getName")或者c1.getMethod("getName", new Class[0])

  4. long starttime = System.currentTimeMillis(); - 获取当前的系统时间(以毫秒为单位),并赋值给变量starttime。这个时间将用作基准测试的起始时间。

  5. for (int i = 0; i < 1000000000; i++) { getName.invoke(user, null); } - 一个循环,它将使用反射调用user对象的getName方法10亿次。invoke方法的第一个参数是要调用该方法的对象,第二个参数是方法的参数(如果有的话)。在这个例子中,getName方法没有参数,所以传递了null。但是,与getMethod调用类似,这里也有一个错误:如果方法没有参数,应该传递一个空数组而不是null。然而,在这种情况下,因为方法确实没有参数,所以可以直接传递null,并且它应该工作正常(尽管传递空数组可能更清晰)。

  6. long endtime = System.currentTimeMillis(); - 在循环执行完成后,再次获取当前的系统时间,并赋值给变量endtime。这个时间将用作基准测试的结束时间。

  7. System.out.println("反射方式执行10亿次,需要时间:" + (endtime - starttime) + "ms"); - 计算基准测试的执行时间(以毫秒为单位),并将其输出到控制台。

(3)这部分代码是另一个Java基准测试,它使用反射来调用User对象的getName方法,但与之前的test02方法有所不同。这里的关键差异在于使用了getDeclaredMethod而不是getMethod,并且调用了setAccessible(true)来关闭Java的访问控制检查,从而提高反射调用的性能。

public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();
    Method getName = c1.getDeclaredMethod("getName", null);
    getName.setAccessible(true);
    long starttime = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
      getName.invoke(user, null);
    }
    long endtime = System.currentTimeMillis();
    System.out.println("关闭检测的反射方式执行10亿次,需要时间:" + (endtime - starttime) + "ms");
  }
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

下面是代码的详细解释:

  1. User user = new User(); - 创建一个User对象实例,并赋值给变量user

  2. Class c1 = user.getClass(); - 调用user对象的getClass方法来获取其运行时类的Class对象,并将其赋值给变量c1

  3. Method getName = c1.getDeclaredMethod("getName", null); - 使用Class对象的getDeclaredMethod方法来获取表示getName方法的Method对象。这里有一个问题:getDeclaredMethod的第二个参数应该是一个Class<?>... parameterTypes的可变参数数组,用来指定方法的参数类型。如果getName方法没有参数,应该传递一个空数组而不是null。正确的调用应该是c1.getDeclaredMethod("getName")或者c1.getDeclaredMethod("getName", new Class[0])

  4. getName.setAccessible(true); - 调用Method对象的setAccessible方法并传递true作为参数,来关闭Java的访问控制检查。这样做可以提高反射的性能,因为它避免了在每次调用方法时都进行安全检查。但是,这样做也有安全风险,因为它允许访问和修改私有方法和字段。

  5. long starttime = System.currentTimeMillis(); - 获取当前的系统时间(以毫秒为单位),并赋值给变量starttime。这个时间将用作基准测试的起始时间。

  6. for (int i = 0; i < 1000000000; i++) { getName.invoke(user, null); } - 一个循环,它将使用反射调用user对象的getName方法10亿次。invoke方法的第一个参数是要调用该方法的对象,第二个参数是方法的参数(如果有的话)。在这个例子中,getName方法没有参数,所以传递了null。但是,与getDeclaredMethod调用类似,这里也有一个错误:如果方法没有参数,应该传递一个空数组而不是null。然而,在这种情况下,因为方法确实没有参数,所以直接传递null也是可以的(尽管传递空数组可能更清晰)。

  7. long endtime = System.currentTimeMillis(); - 在循环执行完成后,再次获取当前的系统时间,并赋值给变量endtime。这个时间将用作基准测试的结束时间。

  8. System.out.println("关闭检测的反射方式执行10亿次,需要时间:" + (endtime - starttime) + "ms"); - 计算基准测试的执行时间(以毫秒为单位),并将其输出到控制台。

总的来说,这段代码的目的是为了测量在Java中使用反射机制并且关闭访问控制检查的情况下重复调用一个对象的方法的性能。关闭访问控制检查可以提高性能,但这样做需要权衡安全性和性能之间的考虑。这个测试可以用来量化这种性能差异,并帮助开发者做出决策。

完整代码

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test07 {
  public static void test01(){
    User user = new User();
    long starttime = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
      user.getName();
    }
    long endtime = System.currentTimeMillis();
    System.out.println("普通方式执行10亿次,需要时间:" + (endtime - starttime) + "ms");
  }

  public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();
    Method getName = c1.getMethod("getName", null);
    long starttime = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
      getName.invoke(user, null);
    }
    long endtime = System.currentTimeMillis();
    System.out.println("反射方式执行10亿次,需要时间:" + (endtime - starttime) + "ms");
  }

  public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();
    Method getName = c1.getDeclaredMethod("getName", null);
    getName.setAccessible(true);
    long starttime = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
      getName.invoke(user, null);
    }
    long endtime = System.currentTimeMillis();
    System.out.println("关闭检测的反射方式执行10亿次,需要时间:" + (endtime - starttime) + "ms");
  }

  public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
    test01();
    test02();
    test03();
  }
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

从代码的执行结果可以看出,关闭安全检测比不关闭时间还长。

分析结果

在Java中,使用反射并关闭安全检测(通过setAccessible(true))通常是为了提高性能,因为它避免了在每次访问字段或调用方法时都进行运行时权限检查。然而,实际上,关闭安全检测并不一定总是导致性能提升,有时甚至可能导致性能下降。这种情况可能由以下几个原因造成:

  1. JVM优化:现代JVM(Java虚拟机)具有高度优化的运行时环境。在某些情况下,JVM可能已经对频繁访问的代码路径进行了内联优化或其他类型的即时编译优化。当关闭安全检查时,这些优化可能会被破坏,因为JVM无法确保代码的安全性,从而可能无法应用某些性能优化。

  2. 测试环境的差异:性能测试的结果可能受到许多因素的影响,包括系统负载、JVM的热身时间(即JIT编译器优化代码所需的时间)、GC(垃圾收集)活动以及其他后台进程。如果在不同的测试运行中环境条件不一致,那么结果可能会有所不同。

  3. 测试方法的实现:测试代码的实现方式可能会影响结果。例如,如果测试方法中包含额外的日志记录、异常处理或其他非反射相关的开销,那么这些开销可能会掩盖或甚至超过反射调用的性能差异。

  4. 方法调用的开销:即使关闭了安全检查,反射调用本身仍然比直接调用方法要慢,因为它涉及到动态解析方法签名、查找方法实现等额外步骤。在大量迭代中,这些额外的步骤可能会累积起来,导致总体性能下降。

  5. 安全检查的实际开销:在某些情况下,安全检查的开销可能并不显著,特别是在方法调用非常频繁的情况下,JVM可能已经通过某种方式优化了这些检查。

  6. 测试误差:性能测试可能受到随机误差的影响,特别是在测量非常小的时间差时。运行多次测试并取平均值通常可以减少这种误差。

  7. 代码预热:JVM的热点代码优化需要时间来进行。如果在性能测试之前没有给JVM足够的时间来预热和优化代码,那么初始的测试结果可能会比之后的运行结果更慢。

  8. 其他JVM设置:JVM的配置和参数设置(如内存分配、垃圾收集器类型等)也可能影响性能测试的结果。

为了获得更准确的结果,建议进行以下操作:

  • 确保在性能测试之前给JVM足够的预热时间。
  • 运行多次测试并取平均值以减少随机误差。
  • 确保测试环境中没有其他显著的性能干扰因素。
  • 仔细审查测试代码,确保除了正在测试的性能差异之外,没有其他额外的开销。
  • 考虑使用专业的性能分析工具(如JProfiler、VisualVM等)来更深入地了解性能瓶颈所在。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Jmeter 4.x 请求到elasticsearch 中文乱码的问题

文章目录 前言解决Jmeter 4.x 请求到elasticsearch 中文乱码的问题 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&#…

Android kotlin 协程异步async与await介绍与使用

一、介绍 在kotlin语言中&#xff0c;协程是一个处理耗时的操作&#xff0c;但是很多人都知道同步和异步&#xff0c;但是不知道该如何正确的使用&#xff0c;如果处理不好&#xff0c;看似异步&#xff0c;其实在runBloacking模块中使用的结果是同步的。 针对如何同步和如何异…

从 MySQL 到 ClickHouse 实时数据同步 —— Debezium + Kafka 表引擎

目录 一、总体架构 二、安装配置 MySQL 主从复制 三、安装配置 ClickHouse 集群 四、安装 JDK 五、安装配置 Zookeeper 集群 六、安装配置 Kafaka 集群 七、安装配置 Debezium-Connector-MySQL 插件 1. 创建插件目录 2. 解压文件到插件目录 3. 配置 Kafka Connector …

快速搭建产品原型

今天要推荐的是一款能协助大家快速搭建产品原型的网站。对应产品经理来说&#xff0c;还真是个利器。 墨刀 https://modao.cc/brand 让想法快速呈现。 画APP原型图&#xff0c;很方便。 快速创建第一个原型文件。 在免费模版上进行修改。 感兴趣的同学去使用体验下吧~ 《Androi…

LabVIEW专栏七、队列

目录 一、队列范例二、命令簇三、队列应用1.1、并行循环队列1.2、命名队列和匿名队列1.2.1、命名队列1.2.2、匿名队列 1.3、长度为1的队列 队列是一种特殊的线性表&#xff0c;就是队列里的元素都是按照顺序进出。 队列的数据元素又称为队列元素。在队列中插入一个队列元素称为…

mysql reset slave reset master

mysql reset slave reset master 1、问题背景2、问题分析3、解决方法3.1、锁定主库&#xff0c;手动同步主库数据到从库&#xff0c;使得主从数据库数据一致3.1、从机执行stop slave、reset slave3.2、从机上再次指定主机的binlog文件名和偏移量3.3、从机执行 start slave3.4、…

蓝牙低能耗安全连接 – 数值比较

除了 LE Legacy 配对之外&#xff0c;LE Secure Connections 是另一种配对选项。 LE 安全连接是蓝牙 v4.2 中引入的增强安全功能。它使用符合联邦信息处理标准 (FIPS) 的算法&#xff08;称为椭圆曲线 Diffie Hellman (ECDH)&#xff09;来生成密钥。对于 LE 安全连接&#xff…

MMSeg搭建模型的坑

Input type(torch.suda.FloatTensor) and weight type (torch.FloatTensor) should be same 自己搭建模型的时候&#xff0c;经常会遇到二者不匹配&#xff0c;以这种情况为例&#xff0c;是因为部分模型没有加载到CUDA上面造成的。 注意搭建模型的时候&#xff0c;所有层都应…

汽车企业安全上网解决方案

需求背景 成立于1866年的某老牌汽车服务独立运营商&#xff0c;目前已经是全球最大的独立汽车服务网络之一&#xff0c;拥有95年的历史&#xff0c;在全球150多个国家拥有17,000多个维修站&#xff0c;始终致力于为每一位车主提供高品质&#xff0c;可信赖的的专业汽车保养和维…

win10加入域环境

win10加入域环境 导航 文章目录 win10加入域环境导航一、关闭防火墙二、使客户端的电脑指向于域控服务器三、检验是否加入了域 一、关闭防火墙 在进行加入域服务之前,我们需要先关闭防火墙(为了不必要的麻烦) 按 winr调出运行窗口,输入 control打开控制面板 点击系统和安全点…

42. UE5 RPG 实现火球术伤害

上一篇&#xff0c;我们解决了火球术于物体碰撞的问题&#xff0c;现在火球术能够正确的和攻击目标产生碰撞。接下来&#xff0c;我们要实现火球术的伤害功能&#xff0c;在火球术击中目标后&#xff0c;给目标造成伤害。 实现伤害功能的思路是给技能一个GameplayEffect&#x…

JAVA毕业设计136—基于Java+Springboot+Vue的房屋租赁管理系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootVue的房屋租赁管理系统(源代码数据库)136 一、系统介绍 本项目前后端分离&#xff0c;分为管理员、用户、工作人员、房东四种角色 1、用户/房东&#xff1a; …

正态性检验

t检验、方差分析&#xff08;ANOVA&#xff09;等参数检验都有一个共同的前提条件&#xff1a;样本数据必须服从正态分布&#xff0c;即样本数据必须来源于一个正态分布的总体&#xff0c;若样本数据不服从正态分布&#xff0c;就不能用以上参数检验对数据进行分析&#xff0c;…

OpenCV鼠标绘制线段

鼠标绘制线段 // 鼠标回调函数 void draw_circle(int event, int x, int y, int flags, void* param) {cv::Mat* img (cv::Mat*)param;if (event cv::EVENT_LBUTTONDBLCLK){cv::circle(*img, cv::Point(x, y), 100, cv::Scalar(0, 0, 255), -1);} }// 鼠标回调函数 void dra…

.NET 个人博客-添加RSS订阅功能

个人博客-添加RSS订阅功能 前言 个人博客系列已经完成了 留言板文章归档推荐文章优化推荐文章排序 博客地址 然后博客开源的原作者也是百忙之中添加了一个名为RSS订阅的功能&#xff0c;那么我就来简述一下这个功能是干嘛的&#xff0c;然后照葫芦画瓢实现一下。 RSS简述…

SpringBoot+RabbitMQ实现MQTT协议通讯

一、简介 MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上&#xff0c;是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议&#xff0c;为此&#xff0c;它需要一个消息中间件 。此…

阿斯达年代记游戏下载教程 阿斯达年代记下载教程

《阿斯达年代记&#xff1a;三强争霸》作为一款气势恢宏的MMORPG大作&#xff0c;是Netmarble与STUDIO DRAGON强强联合的巅峰创作&#xff0c;定于4月24日迎来全球玩家热切期待的公测。游戏剧情围绕阿斯达大陆的王权争夺战展开&#xff0c;三大派系——阿斯达联邦、亚高联盟及边…

浅谈菊风实时音视频 (RTC)与实时操作系统 (RTOS) 在智能硬件领域应用

近年来&#xff0c;菊风通过实时音视频赋能智能手表、智能门禁、智能门锁/门铃、智能眼镜等数十种智能硬件&#xff0c;与一众合作伙伴共同探索在IoT智能硬件领域的不同场景应用&#xff0c;积累了丰富的实践经验。在智能硬件中&#xff0c;RTOS因其轻量化的系统内核&#xff0…

使用Mybatis映射时间 DateTime ==> LocalDateTime

首先查看&#xff0c;数据库字段&#xff1a; 书写映射实体类对象VO&#xff1a; Data public class OrderListVO implements Serializable {private Integer orderId;private String memberName;private String orderNumber;private BigDecimal orderPrice;private String l…

element-ui upload 组件 手动多次出发 submit

element 上传组件 upload 上传成功以后&#xff0c;想重新 调用 submit()函数&#xff0c;发现是不可以进行多次触发的,。 直接上解决方法&#xff0c;在上传成功后的钩子函数里添加:fileList[0l.status ready fileList是文件列表&#xff0c;status是单文件的状态改成ready就…