Java反射原理及其性能优化
目录
- JVM是如何实现反射的
- 反射的性能开销体现在哪里
- 如何优化反射性能开销
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;}}
}
在第一次调用反射时,noInflation为false,这时会生成一个委派实现,而委派实现的具体实现便是一个本地实现。反射调用在进入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.forName、Class.getMethod以及Method.invoke三个操作。其中Class.forName调用本地方法,Class.getMethod则遍历该类的公有方法,如果没有匹配到,还将遍历父类的公有方法。这两个操作都是非常耗时的。
需要注意的是,以getMethod为代表的查找方法操作,会返回查找结果的一份拷贝。因此,应避免在热点代码中使用返回Method数组的getMethods或getDeclaredMethods方法,以减少不必要的堆空间消耗。
在实践中,通常会在应用程序中缓存Class.forName和Class.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倍。
性能开销来源
反射调用的性能开销主要来自以下几个方面:
- 方法表查找
- 构建
Object数组以及可能存在的自动装拆箱操作 - 运行时权限检查
- 可能没有方法内联/逃逸分析
3. 如何优化反射性能开销?
反射性能优化策略:
- 尽量避免反射调用虚方法:虚方法调用的性能开销更大。
- 关闭运行时权限检查:使用
setAccessible(true)可以提升性能。 - 扩大基本数据类型对应的包装类缓存:可通过参数
-Djava.lang.Integer.IntegerCache.high=128来实现。 - 关闭Inflation机制:直接动态生成字节码。
- 提高JVM关于每个调用能够记录的类型数目:通过虚拟机参数
-XX:TypeProfileWidth设置更大的值。
通过上述方法,可以有效减少反射调用的性能开销,提高程序的整体性能。在实际开发中,根据具体场景灵活应用这些优化策略,可以显著提升反射操作的效率。
相关文章:
Java反射原理及其性能优化
目录 JVM是如何实现反射的反射的性能开销体现在哪里如何优化反射性能开销 1. JVM是如何实现反射的? 反射是Java语言中的一种强大功能,它允许程序在运行时动态地获取类的信息以及操作对象。下面是一个简单的示例,演示了如何使用反射调用方法ÿ…...
RabbitMQ 管理平台(控制中心)的介绍
文章目录 一、RabbitMQ 管理平台整体介绍二、Overview 总览三、Connections 连接四、Channels 通道五、Exchanges 交换机六、Queues 队列查看队列详细信息查看队列的消息内容 七、Admin 用户给用户分配虚拟主机 一、RabbitMQ 管理平台整体介绍 RabbitMQ 管理平台内有六个模块&…...
【SQL】在 SQL Server 中创建数据源是 MySQL 数据表的视图
背景:Windows系统已安装了mysql5.7和sqlServer数据库,现在需要在sqlServer创建视图或者查询来自mysql的数据,视图的数据来源mysql数据库。下面进行实现在sqlserver实现获取mysql数据表数据构建视图。 1、打开 ODBC 数据源管理器,…...
现代Web开发:Next.js 深度解析与最佳实践
💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 现代Web开发:Next.js 深度解析与最佳实践 现代Web开发:Next.js 深度解析与最佳实践 现代Web开发…...
LeetCode题练习与总结:赎金信--383
一、题目描述 给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以,返回 true ;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 示例 1࿱…...
eval: jdk1.8.0_431/jre/bin/java: Permission denied
当您在启动Tomcat或其他Java应用时遇到“Permission denied”错误,这通常表示当前用户没有执行指定Java可执行文件的权限。以下是解决这个问题的几种方法: 方法一:检查文件权限 查看文件权限: 使用ls -l命令查看Java可执行文件的…...
.Net IOC理解及代码实现
IOC理解 IoC(Inversion of Control):即控制反转,这是一种设计思想,指将对象的控制权交给IOC容器,由容器来实现对象的创建、管理,程序员只需要从容器获取想要的对象就可以了。DI(Dependency Injection),即依…...
履带机器人(一、STM32控制部分--标准库)
一、履带机器人整体逻辑框架 通过在PC端搭建上位机,使得在PC端可以给STM32发送控制指令并且接受STM32的状态信息。 通过RS485通信,使得STM32可以和电机进行通信,STM32发送启动、停止、转速、方向等指令,并接受电机返回的状态信息。 二、STM32逻辑框架 整体逻辑: 1、先…...
地理空间-Java实现航迹稀释
Java实现航迹点稀释算法(Douglas - Peucker算法)的示例代码,该算法可在保证航迹整体形状变化不大的情况下减少航迹点数量: 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消息,允许在单个HTTP请求中包含多个数据部分,如文件、文本等。这种多部分请求在上传文件或发送带有附件的邮件等场景中非常有用。QHttpMultiPart类…...
【测试】【Debug】vscode中同一个测试用例出现重复
这种是正常的情况 当下面又出现一个 类似python_test->文件夹名->test_good ->test_pad 同一个测试用例出现两次,名称都相同,显然是重复了。那么如何解决? 这种情况是因为在终端利用“pip install pytest”安装 之后,又…...
Mac上的免费压缩软件-FastZip使用体验实测
FastZip是Mac上的一款免费的压缩软件,分享一下我在日常使用中的体验 压缩格式支持7Z、Zip,解压支持7Z、ZIP、RAR、TAR、GZIP、BZIP2、XZ、LZIP、ACE、ISO、CAB、PAX、JAR、AR、CPIO等所有常见格式的解压 体验使用下来能满足我所有的压缩与解压的需求&a…...
Linux(CentOS)运行 jar 包
1、在本地终端运行,关闭终端,程序就会终止 java -jar tlias-0.0.1-SNAPSHOT.jar 发送请求,成功 关闭终端(程序也会终止) 发送请求,失败 2、在远程终端运行,关闭终端,程序就会终止 …...
基于YOLOv8 Web的安全帽佩戴识别检测系统的研究和设计,数据集+训练结果+Web源码
摘要 在工地,制造工厂,发电厂等地方,施工人佩戴安全帽能有效降低事故发生概率,在工业制造、发电等领域需要进行施工人员安全帽监测。目前大多数的 YOLO 模型还拘泥于公司、企业开发生产的具体产品中,大多数无编程基础…...
LabVIEW VISA通信常见问题
在工业自动化和测试测量等应用中,使用LabVIEW的VISA函数与设备进行通信时,若发送指令后未能接收数据,以下因素可能是原因: 设备未响应或响应延迟应用示例:例如,在控制测量仪器(如电压表…...
Node.js Stream(流)以及模块系统使用介绍 (基础介绍 五)
Stream(流) Stream 是 Node.js 中非常重要的一个模块,应用广泛。 Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出…...
嵌入式linux中设备树控制硬件的方法
大家好,今天主要给大家分享一下,如何使用linux系统下的设备树进行硬件控制方法。 第一:linux系统中设备树驱动LED原理 在linux系统中可以使用设备树向Linux内核传递相关的寄存器地址,linux驱动中使用OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关…...
定时器入门:Air780E定时器基础与进阶
今天我们学习的是Air780E定时器基础与进阶,让大家更深入的了解定时器。 一、定时器(timer)的概述 在Air780E模组搭载的LuatOS系统中,定时器(timer)是一项基础且关键的服务。它允许开发者在特定的时间点或周期性地执行代码段&…...
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客户端库,它提供了细化的API,对每个Redis命令的功能进行了封装,使得用户只需记住命令,具体的用法可以直接查看接口的声明,使用成本较低。go-redis对数据类型按照Redis底…...
MongoDB中大型文本字段怎么存_GridFS切分与外部存储对比
会。MongoDB单文档上限16MB,但超2MB字符串易致客户端OOM或超时;GridFS非自动魔法,需手动管理分块、拼接与清理;大文本应优先存OSS/S3,Mongo仅存元数据。大文本存MongoDB会撑爆内存吗?会。MongoDB单文档上限…...
鸣潮智能助手:提升游戏体验的AI解决方案
鸣潮智能助手:提升游戏体验的AI解决方案 【免费下载链接】ok-wuthering-waves 鸣潮 后台自动战斗 自动刷声骸 一键日常 Automation for Wuthering Waves 项目地址: https://gitcode.com/GitHub_Trending/ok/ok-wuthering-waves 您是否曾遇到这样的困扰&#…...
教授专栏204| 潘永安:成功研发新型光探测器,促进可编程光子学应用
港科大电子及计算机工程学系系主任及教授潘永安(左)丶博士生牛玥(右)于港科大光子器件实验室合照。可编程光子学利用光传送讯号,能达到比电子学更快丶更节能的运算。然而,现有片上功率监测器的性能不足&…...
三步永久保存青春记忆:GetQzonehistory让QQ空间数据永不消逝
三步永久保存青春记忆:GetQzonehistory让QQ空间数据永不消逝 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾担心多年积累的QQ空间说说突然消失?那些承载…...
万兆光模块:网络提速的核心引擎
在数字化转型的浪潮中,数据已成为核心生产要素,而连接数据的网络,则是决定其流动速度与效率的关键。当我们沉浸在4K/8K的视觉盛宴中,惊叹于云游戏的即时交互,或是受益于远程医疗的精准诊断时,背后都离不开一…...
旧Mac焕新:使用OpenCore Legacy Patcher让2008-2017年设备支持最新macOS系统
旧Mac焕新:使用OpenCore Legacy Patcher让2008-2017年设备支持最新macOS系统 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 老旧设备升级正成为越…...
从IDEA到K8s:飞算JavaAI如何打通微服务开发的“最后一公里”
云原生时代的一站式开发革命:当JavaAI遇上Kubernetes 在数字化转型的浪潮中,微服务架构已成为企业技术栈的标配,但随之而来的开发复杂度却让许多团队陷入"最后一公里"困境。传统开发流程中,从本地编码到云端部署需要跨…...
React Native测试配置终极指南:Jest与React Testing Library完整实战
React Native测试配置终极指南:Jest与React Testing Library完整实战 【免费下载链接】react-native-boilerplate A React Native template for building solid applications 🐙, using JavaScript 💛 or Typescript 💙 (you choo…...
终极指南:如何将Python PEG语法轻松迁移到Ohm解析器
终极指南:如何将Python PEG语法轻松迁移到Ohm解析器 【免费下载链接】ohm A library and language for building parsers, interpreters, compilers, etc. 项目地址: https://gitcode.com/gh_mirrors/oh/ohm 想要将Python的PEG语法迁移到Ohm解析器框架吗&…...
基于Simulink的滞环电压控制(Bang-Bang)Buck仿真
目录 手把手教你学Simulink ——基于Simulink的滞环电压控制(Bang-Bang)Buck仿真 一、问题背景 二、滞环控制原理 1. 控制思想 三、系统架构 四、Simulink 建模步骤 第一步:搭建 Buck 主电路 第二步:实现滞环比较器 第三步:死区时间插入(防直通) 第四步:驱动…...
