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底…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
