当前位置:网站首页>Reflection tricks can boost your performance by N times
Reflection tricks can boost your performance by N times
2022-07-30 09:20:00 【hi-dhl】
在之前的文章和视频中我们拆分了不同的场景对比反射的性能.
- 文字版: 侧重于细节上的知识点更多、更加详细,揭秘反射真的很耗时吗,射 10 万次耗时多久
- 视频版: 通过动画展示讲解,更加的清楚、直观,视频版本 bilibili 地址: b23.tv/Hprua24
在之前的文章中提到了一个提升性能非常重要的点,将 Accessible 设置 true 反射速度会进一步提升,如果单看一个程序,可能这点性能微不足道,但是如果放在一个大的复杂的工程下面,运行在大量的低端机下,一行代码提升的性能,可能比你写 100 行代码提升的性能更加显著.
而今天这篇文章从源码的角度分析一下 isAccessible() 方法的作用,为什么将 Accessible 设置为 true 可以提升性能,在开始分析之前,我们先写一段代码.
- 声明一个普通类,里面有个
public方法getName()和private方法getAddress()
class Person {
public fun getName(): String {
return "I am DHL"
}
private fun getAddress(): String {
return "BJ"
}
}
- 通过反射获取
getName()和getAddress()方法,花 3 秒钟思考一下,下面的代码输出的结果
// public 方法
val method1 = Person::class.declaredFunctions.find { it.name == "getName" }
println("access = ${method1?.isAccessible}")
// private 方法
val method2 = Person::class.declaredFunctions.find { it.name == "getAddress" }
println("access = ${method2?.isAccessible}")
无论是调用 public getName() 方法还是调用 private getAddress() 方法,最后输出的结果都为 false,通过这个例子也间接说明了 isAccessible() 方法并不是用来表示访问权限的.
当我们通过反射调用 private 方法时,都需要执行 setAccessible() 方法设置为 true, 否者会抛出下面的异常.
java.lang.IllegalAccessException: can not access a member of class com.hi.dhl.demo.reflect.Person
如果通过反射调用 public 方法,不设置 Accessible 为 true,也可以正常调用,所以有很多小伙伴认为 isAccessible() 方法用来表示访问权限,其实这种理解是错误的.
我们一起来看一下源码是如何解释的,方法 isAccessible() 位于 AccessibleObject 类中.
public class AccessibleObject implements AnnotatedElement {
......
// NOTE: for security purposes, this field must not be visible
boolean override;
public boolean isAccessible() {
return override;
}
public void setAccessible(boolean flag) throws SecurityException {
......
}
......
}
AccessibleObject 是 Field 、 Method 、 Constructor 的父类,调用 isAccessible() 返回 override 的值,而字段 override 主要判断是否要进行安全检查.
字段 override 在 AccessibleObject 子类当中使用,所以我们一起来看一下它的子类 Method.
public Object invoke(Object obj, Object... args){
// 是否要进行安全检查
if (!override) {
// 进行快速验证是否是 Public 方法
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
// 返回调用这个方法的 Class
Class<?> caller = Reflection.getCallerClass();
// 做权限访问的校验,缓存调用这个方法的 Class,避免下次在做检查
checkAccess(caller, clazz, obj, modifiers);
}
}
......
return ma.invoke(obj, args);
}
字段 override 提供给子类去重写,它的值决定了是否要进行安全检查,如果要进行安全检查,则会执行 quickCheckMemberAccess() 快速验证是否是 Public 方法,避免调用 getCallerClass().
- 如果是
Public方法,避免做安全检查,所以我们在代码中不调用setAccessible(true)方法,也不会抛出异常 - 如果不是
Public方法则会调用getCallerClass()获取调用这个方法的 Class,执行checkAccess()方法进行安全检查.
// it is necessary to perform somewhat expensive security checks.
// A more complicated security check cache is needed for Method and Field
// The cache can be either null (empty cache)
volatile Object securityCheckCache; // 缓存调用这个方法的 Class
void checkAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers){
......
Object cache = securityCheckCache; // read volatile
if(cache == 调用这个方法的 Class){
return; // ACCESS IS OK
}
slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
......
}
void slowCheckMemberAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers,Class<?> targetClass){
Reflection.ensureMemberAccess(caller, clazz, obj, modifiers);
Object cache = 调用这个方法的 Class
securityCheckCache = cache; // 缓存调用这个方法的 Class
}
源码中注释也说明了,如果要进行安全检查那么它的代价是非常昂贵的,所以用变量 securityCheckCache 缓存调用这个方法的 Class.如果下次使用相同的 Class,就不需要在做安全检查,但是这个缓存有个缺陷,如果换一个调用这个方法的 Class,需要再次做安全检查,并且会覆盖之前的缓存结果.
如果要在运行时修改属性或者调用某个方法时,都要进行安全检查,而安全检查是非常消耗资源的,所以 JDK 提供了一个 setAccessible() 方法,可以绕过安全检查,让开发者自己来决定是否要避开安全检查.
因为反射本身是非常慢的,如果能够避免安全检查,可以进一步提升性能,在之前的文章 揭秘反射真的很耗时吗,射 10 万次耗时多久,针对不同场景,分别测试了反射前后以及关闭安全检查的耗时.
| 正常调用 | 反射 | 反射优化后 | 反射优化后关掉安全检查 | |
|---|---|---|---|---|
| 创建对象 | 0.578 ms/op | 4.710 ms/op | 1.018 ms/op | 0.943 ms/op |
| 方法调用 | 0.422 ms/op | 10.533 ms/op | 0.844 ms/op | 0.687 ms/op |
| 属性调用 | 0.241 ms/op | 12.432 ms/op | 1.362 ms/op | 1.202 ms/op |
| 伴生对象 | 0.470 ms/op | 5.661 ms/op | 0.840 ms/op | 0.702 ms/op |
从测试结果可以看出来,执行 setAccessible() 方法,设置为 true 关掉安全检查之后,反射速度得到了进一步的提升,更接近于正常调用.
全文到这里就结束了,感谢你的阅读,坚持原创不易,欢迎在看、点赞、分享给身边的小伙伴,我会持续分享原创干货!!!
近期必读热门文章
边栏推荐
- 剖析SGI STL空间配置器(一 、辅助接口函数)
- How to Assemble a Registry
- 【三子棋】——玩家VS电脑(C语言实现)
- The R installation package has error in rawtochar(block[seq_len(ns)]) :
- SEER数据库中“手术变量(RX SUMM-SURG OTH REG/DIS )”下的字段解释
- typescript1-typescript是什么
- 如何使用 Jmeter 进行抢购、秒杀等场景下,进行高并发?
- 积分简明笔记-第一类曲线积分的类型
- Kubernetes 在科技革命中的演变
- tabindex attribute of input tag & tabindex attribute of a tag
猜你喜欢

【愚公系列】2022年07月 Go教学课程 021-Go容器之切片操作

Alibaba Cloud Cloud Server Firewall Settings

剖析SGI STL空间配置器(空间配置器的重要性和重要成员及函数)

typescript2-typescript为什么给js添加类型支持

ACL 2022 | 引入角度margin构建对比学习目标,增强文本语义判别能力

Why does typescript2-typescript add type support to js

How to Assemble a Registry

EMC过不了?都是PCB工程师的锅?

SQL row-column conversion
【SQL server速成之路】——身份验证及建立和管理用户账户
随机推荐
MagicDraw secondary development process
Hands-on teaching OneOS FOTA upgrade
Lenovo Notebook How to Change Windows 10 Boot Logo Icon
蓝牙技术|了解蓝牙LE Audio的Auracast广播音频
test3
万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(另附课设大作业参考)
HashSet and LinkedHashSet
基于JSP实现校园二手交易平台
It is said that FPGA is high-end, what can it do?
hcip第八天
电源完整性基础知识
浅论各种调试接口(JTAG、SWD、RDI、Jlink、Ulink、STlink)的区别
Webview中的超链接点击到外部浏览器打开
How to use Jmeter to carry out high concurrency in scenarios such as panic buying and seckill?
ant-design form表单校验upload组件(附个人封装的上传组件)
Golang DES 加解密如何实现?
[Mini Program Column] Summarize the development specifications of uniapp to develop small programs
Dynamic Lead Time Promising
The blockbuster IP that has been popular in the world for 25 years, the new work has become a script paradise
如何组装一个注册中心