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底…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
