当前位置: 首页 > news >正文

Java反射原理及其性能优化

目录

  1. JVM是如何实现反射的
  2. 反射的性能开销体现在哪里
  3. 如何优化反射性能开销

1. JVM是如何实现反射的?

反射是Java语言中的一种强大功能,它允许程序在运行时动态地获取类的信息以及操作对象。下面是一个简单的示例,演示了如何使用反射调用方法:

public class Solution {public static void show(int i) {new Exception("#" + i).printStackTrace();}public static void main(String[] args) throws Exception {Class<?> clazz = Class.forName("Solution");Method method = clazz.getMethod("show", int.class);method.invoke(null, 0);}
}

在上述代码中,我们使用Method.invoke来执行反射方法调用,并通过打印show方法的栈轨迹来观察调用的类。输出如下:

java.lang.Exception: #0at Solution.show(Solution.java:15)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at Solution.main(Solution.java:21)

首先我们看一下Method.invoke的实现:

public final class Method extends Executable {...public Object invoke(Object obj, Object... args) throws ... {... // 权限检查MethodAccessor ma = methodAccessor;if (ma == null) {ma = acquireMethodAccessor();}return ma.invoke(obj, args);}
}

可以看到,实际上它是委派给了MethodAccessor来处理。MethodAccessor是一个接口,具有两个具体实现:一个是通过本地方法(NativeMethodAccessorImpl)来实现的,称为本地实现;另一个是使用了委派模式(DelegatingMethodAccessorImpl),称为委派实现。

MethodAccessor实例的创建

MethodAccessor实例是在ReflectionFactory中创建的:

public class ReflectionFactory {...public MethodAccessor newMethodAccessor(Method method) {checkInitted();...if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {return new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(),method.getName(),method.getParameterTypes(),method.getReturnType(),method.getExceptionTypes(),method.getModifiers());} else {NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);acc.setParent(res);return res;}}
}

在第一次调用反射时,noInflationfalse,这时会生成一个委派实现,而委派实现的具体实现便是一个本地实现。反射调用在进入Java虚拟机内部后,实际是调用目标方法的具体地址。

动态生成字节码的实现

Java的反射调用机制还设立了另一种动态生成字节码的实现(简称动态实现),直接使用invoke指令来调用目标方法。动态实现的运行效率要快20倍,因为它避免了Java到C++再到Java的切换,但由于生成字节码非常耗时,仅调用一次的话,本地实现反而要快3到4倍。

Java虚拟机设置了一个阈值15,当某个反射调用的次数在15之下时,采用本地实现;当达到15时,开始动态生成字节码并切换至动态实现,这个过程称为Inflation。

Inflation机制

NativeMethodAccessorImpl中每次invoke方法被调用时,都会增加一次计时器,并判断是否超过阈值,超过后调用MethodAccessorGenerator.generateMethod()生成Java版的MethodAccessor实现类,并改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。

小结

在默认情况下,方法的反射调用为委派实现,调用超过15次后,委派实现便会切换至动态实现,该动态实现的字节码是自动生成的,将直接使用invoke指令调用目标方法。可以通过参数-Dsun.reflect.noInflation=true来关闭Inflation机制,直接生成动态实现。

2. 反射的性能开销体现在哪里?

在上面的例子中,我们使用了Class.forNameClass.getMethod以及Method.invoke三个操作。其中Class.forName调用本地方法,Class.getMethod则遍历该类的公有方法,如果没有匹配到,还将遍历父类的公有方法。这两个操作都是非常耗时的。

需要注意的是,以getMethod为代表的查找方法操作,会返回查找结果的一份拷贝。因此,应避免在热点代码中使用返回Method数组的getMethodsgetDeclaredMethods方法,以减少不必要的堆空间消耗。

在实践中,通常会在应用程序中缓存Class.forNameClass.getMethod的结果,因此下面我们只关注反射调用本身的性能开销。

public class ReflectDemo {public void doSth(int i) {}public static void main(String[] args) throws Exception {Class<?> clazz = ReflectDemo.class;Constructor constructor = clazz.getConstructor();Object object = constructor.newInstance();Method method = clazz.getMethod("doSth", int.class);ReflectDemo demo = new ReflectDemo();long current = System.currentTimeMillis();for (int i = 1; i <= 2_000_000_000; i++) {if (i % 100_000_000 == 0) {long temp = System.currentTimeMillis();System.out.println(temp - current);current = temp;}// 直接调用demo.doSth(2333);// 反射调用// method.invoke(object, 2333);}}
}

根据测试结果,一亿次的直接调用耗时为91.6ms,而反射调用耗时281.6ms,约为基准值的3.07倍。

性能开销来源

反射调用的性能开销主要来自以下几个方面:

  1. 方法表查找
  2. 构建Object数组以及可能存在的自动装拆箱操作
  3. 运行时权限检查
  4. 可能没有方法内联/逃逸分析

3. 如何优化反射性能开销?

反射性能优化策略:

  1. 尽量避免反射调用虚方法:虚方法调用的性能开销更大。
  2. 关闭运行时权限检查:使用setAccessible(true)可以提升性能。
  3. 扩大基本数据类型对应的包装类缓存:可通过参数-Djava.lang.Integer.IntegerCache.high=128来实现。
  4. 关闭Inflation机制:直接动态生成字节码。
  5. 提高JVM关于每个调用能够记录的类型数目:通过虚拟机参数-XX:TypeProfileWidth设置更大的值。

通过上述方法,可以有效减少反射调用的性能开销,提高程序的整体性能。在实际开发中,根据具体场景灵活应用这些优化策略,可以显著提升反射操作的效率。

相关文章:

Java反射原理及其性能优化

目录 JVM是如何实现反射的反射的性能开销体现在哪里如何优化反射性能开销 1. JVM是如何实现反射的? 反射是Java语言中的一种强大功能&#xff0c;它允许程序在运行时动态地获取类的信息以及操作对象。下面是一个简单的示例&#xff0c;演示了如何使用反射调用方法&#xff…...

RabbitMQ 管理平台(控制中心)的介绍

文章目录 一、RabbitMQ 管理平台整体介绍二、Overview 总览三、Connections 连接四、Channels 通道五、Exchanges 交换机六、Queues 队列查看队列详细信息查看队列的消息内容 七、Admin 用户给用户分配虚拟主机 一、RabbitMQ 管理平台整体介绍 RabbitMQ 管理平台内有六个模块&…...

【SQL】在 SQL Server 中创建数据源是 MySQL 数据表的视图

背景&#xff1a;Windows系统已安装了mysql5.7和sqlServer数据库&#xff0c;现在需要在sqlServer创建视图或者查询来自mysql的数据&#xff0c;视图的数据来源mysql数据库。下面进行实现在sqlserver实现获取mysql数据表数据构建视图。 1、打开 ODBC 数据源管理器&#xff0c;…...

现代Web开发:Next.js 深度解析与最佳实践

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 现代Web开发&#xff1a;Next.js 深度解析与最佳实践 现代Web开发&#xff1a;Next.js 深度解析与最佳实践 现代Web开发&#xf…...

LeetCode题练习与总结:赎金信--383

一、题目描述 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 示例 1&#xff1…...

eval: jdk1.8.0_431/jre/bin/java: Permission denied

当您在启动Tomcat或其他Java应用时遇到“Permission denied”错误&#xff0c;这通常表示当前用户没有执行指定Java可执行文件的权限。以下是解决这个问题的几种方法&#xff1a; 方法一&#xff1a;检查文件权限 查看文件权限&#xff1a; 使用ls -l命令查看Java可执行文件的…...

.Net IOC理解及代码实现

IOC理解 IoC(Inversion of Control)&#xff1a;即控制反转&#xff0c;这是一种设计思想&#xff0c;指将对象的控制权交给IOC容器&#xff0c;由容器来实现对象的创建、管理&#xff0c;程序员只需要从容器获取想要的对象就可以了。DI(Dependency Injection)&#xff0c;即依…...

履带机器人(一、STM32控制部分--标准库)

一、履带机器人整体逻辑框架 通过在PC端搭建上位机,使得在PC端可以给STM32发送控制指令并且接受STM32的状态信息。 通过RS485通信,使得STM32可以和电机进行通信,STM32发送启动、停止、转速、方向等指令,并接受电机返回的状态信息。 二、STM32逻辑框架 整体逻辑: 1、先…...

地理空间-Java实现航迹稀释

Java实现航迹点稀释算法&#xff08;Douglas - Peucker算法&#xff09;的示例代码&#xff0c;该算法可在保证航迹整体形状变化不大的情况下减少航迹点数量&#xff1a; import java.util.ArrayList; import java.util.List; class Point { double x; double y; public Point…...

qt QHttpMultiPart详解

1. 概述 QHttpMultiPart是Qt框架中用于处理HTTP多部分请求的类。它类似于RFC 2046中描述的MIME multipart消息&#xff0c;允许在单个HTTP请求中包含多个数据部分&#xff0c;如文件、文本等。这种多部分请求在上传文件或发送带有附件的邮件等场景中非常有用。QHttpMultiPart类…...

【测试】【Debug】vscode中同一个测试用例出现重复

这种是正常的情况 当下面又出现一个 类似python_test->文件夹名->test_good ->test_pad 同一个测试用例出现两次&#xff0c;名称都相同&#xff0c;显然是重复了。那么如何解决&#xff1f; 这种情况是因为在终端利用“pip install pytest”安装 之后&#xff0c;又…...

Mac上的免费压缩软件-FastZip使用体验实测

FastZip是Mac上的一款免费的压缩软件&#xff0c;分享一下我在日常使用中的体验 压缩格式支持7Z、Zip&#xff0c;解压支持7Z、ZIP、RAR、TAR、GZIP、BZIP2、XZ、LZIP、ACE、ISO、CAB、PAX、JAR、AR、CPIO等所有常见格式的解压 体验使用下来能满足我所有的压缩与解压的需求&a…...

Linux(CentOS)运行 jar 包

1、在本地终端运行&#xff0c;关闭终端&#xff0c;程序就会终止 java -jar tlias-0.0.1-SNAPSHOT.jar 发送请求&#xff0c;成功 关闭终端&#xff08;程序也会终止&#xff09; 发送请求&#xff0c;失败 2、在远程终端运行&#xff0c;关闭终端&#xff0c;程序就会终止 …...

基于YOLOv8 Web的安全帽佩戴识别检测系统的研究和设计,数据集+训练结果+Web源码

摘要 在工地&#xff0c;制造工厂&#xff0c;发电厂等地方&#xff0c;施工人佩戴安全帽能有效降低事故发生概率&#xff0c;在工业制造、发电等领域需要进行施工人员安全帽监测。目前大多数的 YOLO 模型还拘泥于公司、企业开发生产的具体产品中&#xff0c;大多数无编程基础…...

LabVIEW VISA通信常见问题

在工业自动化和测试测量等应用中&#xff0c;使用LabVIEW的VISA函数与设备进行通信时&#xff0c;若发送指令后未能接收数据&#xff0c;以下因素可能是原因&#xff1a; 设备未响应或响应延迟应用示例&#xff1a;例如&#xff0c;在控制测量仪器&#xff08;如电压表&#xf…...

Node.js Stream(流)以及模块系统使用介绍 (基础介绍 五)

Stream(流) Stream 是 Node.js 中非常重要的一个模块&#xff0c;应用广泛。 Stream 是一个抽象接口&#xff0c;Node 中有很多对象实现了这个接口。例如&#xff0c;对http 服务器发起请求的request 对象就是一个 Stream&#xff0c;还有stdout&#xff08;标准输出&#xf…...

嵌入式linux中设备树控制硬件的方法

大家好,今天主要给大家分享一下,如何使用linux系统下的设备树进行硬件控制方法。 第一:linux系统中设备树驱动LED原理 在linux系统中可以使用设备树向Linux内核传递相关的寄存器地址,linux驱动中使用OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关…...

定时器入门:Air780E定时器基础与进阶

今天我们学习的是Air780E定时器基础与进阶&#xff0c;让大家更深入的了解定时器。 一、定时器(timer)的概述 在Air780E模组搭载的LuatOS系统中&#xff0c;定时器&#xff08;timer&#xff09;是一项基础且关键的服务。它允许开发者在特定的时间点或周期性地执行代码段&…...

Java LeetCode练习

3216. 交换后字典序最小的字符串 package JavaExercise;public class Exercise {public static void main(String[] args) {String s "45320";Solution solution new Solution();System.out.println(solution.getSmallestString(s));} }class Solution {public St…...

go 集成go-redis 缓存操作

一、什么是Go Redis 这是一个流行的Go语言Redis客户端库&#xff0c;它提供了细化的API&#xff0c;对每个Redis命令的功能进行了封装&#xff0c;使得用户只需记住命令&#xff0c;具体的用法可以直接查看接口的声明&#xff0c;使用成本较低。go-redis对数据类型按照Redis底…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...