java 反射 速度_Java反射,但速度更快

java 反射 速度

在编译时不知道Java类的最快方法是什么? Java框架通常会这样做。 很多。 它可以直接影响其性能。 因此,让我们对不同的方法进行基准测试,例如反射,方法句柄和代码生成。

用例

假设我们有一个简单的Person类,其中包含名称和地址:

public class Person {...public String getName() {...}public Address getAddress() {...}}

并且我们想使用诸如以下的框架:

  • XStream ,JAXB或Jackson来将实例序列化为XML或JSON。
  • JPA /Hibernate将人员存储在数据库中。
  • OptaPlanner分配地址(如果他们是游客或无家可归的人)。

这些框架都不了解Person类。 因此,他们不能简单地调用person.getName()

// Framework codepublic Object executeGetter(Object object) {// Compilation error: class Person is unknown to the frameworkreturn ((Person) object).getName();}

相反,代码使用反射,方法句柄或代码生成。

但是这样的代码被称为很多

  • 如果在数据库中插入1000个不同的人员,则JPA / Hibernate可能会调用2000次这样的代码:
    • 1000次调用Person.getName()
  • 同样,如果您用XML或JSON编写1000个不同的人,则XStream,JAXB或Jackson可能会进行2000次调用。

显然,当这种代码每秒被调用x次时, 其性能很重要

基准测试

使用JMH,我在带有32GB RAM的64位8核Intel i7-4790台式机上的Linux上使用OpenJDK 1.8.0_111运行了一组微型基准测试。 JMH基准测试有3个分支,1秒的5个预热迭代和1秒的20个测量迭代。

该基准测试的源代码位于此GitHub存储库中 。

TL; DR结果

  • Java反射很慢。 (*)
  • Java MethodHandles也很慢。 (*)
  • javax.tools生成的代码很快。 (*)

(*)在用例中,我以使用的工作量作为基准。 你的旅费可能会改变。

因此,魔鬼在细节中。 让我们浏览一下实现,以确认我应用了典型的魔术技巧(例如setAccessible(true) )。

实作

直接访问(基准)

我使用了一个普通的person.getName()调用作为基准:

public final class MyAccessor {public Object executeGetter(Object object) {return ((Person) object).getName();}}

每次操作大约需要2.7纳秒:

Benchmark           Mode  Cnt  Score   Error  Units
===================================================
DirectAccess        avgt   60  2.667 ± 0.028  ns/op

直接访问自然是运行时最快的方法,而没有引导成本。 但是它在编译时导入Person ,因此每个框架都无法使用它。

反射

框架在运行时读取吸气剂而无需事先知道的明显方法是通过Java Reflection:

public final class MyAccessor {private final Method getterMethod;public MyAccessor() {getterMethod = Person.class.getMethod("getName");// Skip Java language access checking during executeGetter()getterMethod.setAccessible(true);}public Object executeGetter(Object bean) {return getterMethod.invoke(bean);}}

添加setAccessible(true)调用可以使这些反射调用更快,但是即使这样,每个调用也要花费5.5纳秒。

Benchmark           Mode  Cnt  Score   Error  Units
===================================================
DirectAccess        avgt   60  2.667 ± 0.028  ns/op
Reflection          avgt   60  5.511 ± 0.081  ns/op

反射比直接访问慢106%(大约慢一倍)。 预热还需要更长的时间。

这对我来说不是什么大惊喜,因为当我使用OptaPlanner在980个城市中描述(使用抽样)一个人为简单的旅行推销员问题时,反射成本像拇指酸痛一样突出:

方法句柄

Java 7中引入了MethodHandle以支持invokedynamic指令。 根据javadoc,它是对基础方法的类型化,直接可执行的引用。 听起来快吧?

public final class MyAccessor {private final MethodHandle getterMethodHandle;public MyAccessor() {MethodHandle temp = lookup.findVirtual(Person.class, "getName", MethodType.methodType(String.class));temp = temp.asType(temp.type().changeParameterType(0 , Object.class));getterMethodHandle = temp.asType(temp.type().changeReturnType(Object.class));}public Object executeGetter(Object bean) {return getterMethodHandle.invokeExact(bean);}}

不幸的是, MethodHandle甚至比 OpenJDK 8中的反射还要慢 。每次操作花费6.1纳秒,因此比直接访问慢132%。

Benchmark           Mode  Cnt  Score   Error  Units
===================================================
DirectAccess        avgt   60  2.667 ± 0.028  ns/op
Reflection          avgt   60  5.511 ± 0.081  ns/op
MethodHandle        avgt   60  6.188 ± 0.059  ns/op
StaticMethodHandle  avgt   60  5.481 ± 0.069  ns/op

话虽如此,如果MethodHandle在静态字段中,则每次操作仅需5.5纳秒,这仍然与反射一样慢 。 此外,对于大多数框架而言,这是无法使用的。 例如,JPA实现可能需要反映n类( PersonCompanyOrder等)的m getter( getName()getAddress()getBirthDate() ,...),因此JPA实现如何有n * m静态字段,在编译时不知道nm

我确实希望MethodHandle在将来的Java版本中能够像直接访问一样快,从而取代对...的需求。

使用javax.tools.JavaCompiler生成的代码

在Java中,可以在运行时编译和运行生成的Java代码。 因此,使用javax.tools.JavaCompiler API,我们可以在运行时生成直接访问代码:

public abstract class MyAccessor {public static MyAccessor generate() {final String String fullClassName = "x.y.generated.MyAccessorPerson$getName";final String source = "package x.y.generated;\n"+ "public final class MyAccessorPerson$getName extends MyAccessor {\n"+ "    public Object executeGetter(Object bean) {\n"+ "        return ((Person) object).getName();\n"+ "    }\n"+ "}";JavaFileObject fileObject = new ...(fullClassName, source);JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();ClassLoader classLoader = ...;JavaFileManager javaFileManager = new ...(..., classLoader)CompilationTask task = compiler.getTask(..., javaFileManager, ..., singletonList(fileObject));boolean success = task.call();...Class compiledClass = classLoader.loadClass(fullClassName);return compiledClass.newInstance();}// Implemented by the generated subclasspublic abstract Object executeGetter(Object object);}

有关如何使用javax.tools.JavaCompiler更多信息,请参见本文或本文的 第2页 。 除了javax.tools之外,类似的方法也可以使用ASM或CGLIB,但是这些方法会推断出额外的依赖性,并且可能会产生不同的性能结果。

无论如何, 生成的代码与直接访问一样快

Benchmark           Mode  Cnt  Score   Error  Units
===================================================
DirectAccess        avgt   60  2.667 ± 0.028  ns/op
GeneratedCode       avgt   60  2.745 ± 0.025  ns/op

因此,当我再次在OptaPlanner中运行该完全相同的Traveling Salesman问题时,这一次使用代码生成来访问计划变量, 因此总分计算速度提高了18% 。 并且分析(使用采样)看起来也更好:

请注意,在正常使用情况下,由于大量CPU需要实际复杂的分数计算,因此性能提升几乎是无法检测到的...

运行时代码生成的唯一缺点是,它会导致可观的引导成本,特别是如果生成的代码未进行批量编译时。 因此,我仍然希望有一天MethodHandles能够像直接访问一样快,只是为了避免启动成本。

结论

在此基准测试中,反射和MethodHandles的速度是OpenJDK 8中直接访问的两倍,但是生成的代码的速度是直接访问的速度。

Benchmark           Mode  Cnt  Score   Error  Units
===================================================
DirectAccess        avgt   60  2.667 ± 0.028  ns/op
Reflection          avgt   60  5.511 ± 0.081  ns/op
MethodHandle        avgt   60  6.188 ± 0.059  ns/op
StaticMethodHandle  avgt   60  5.481 ± 0.069  ns/op
GeneratedCode       avgt   60  2.745 ± 0.025  ns/op

翻译自: https://www.javacodegeeks.com/2018/01/java-reflection-much-faster.html

java 反射 速度

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/334247.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

macOS 内核之 OS X 系统的起源

文章目录一、苹果公司早期(1972-1991)二、苹果在操作系统上的尝试(1991-1997)2.1 Star Trek 项目 (1992-1993)2.2 Copland-Mac OS 8 (1994-1996)三、收购与转折(1996-1997)四、NeXT 篇章4.1 NEXTSTEP(1985-1997)4.2 OpenStep(1993-1997)五、Mach 的历史5.1 Rochester’s Intell…

docker create_Docker动手教程2.2:容器基本操作2

内容摘要暂停/取消暂停容器删除容器进入容器创建容器暂停/取消暂停容器暂停容器命令:docker pause 容器ID/容器名注意STATUS列,被暂停的容器的状态依旧是“Up”,但是后面括号显示为“Paused”。取消暂停命令:docker unpause 容器I…

c语言全局变量和局部变量作用域重合时,c语言全局变量与局部变量(当变量重名时)的使用情况...

ABP框架 - 时间文档目录 本节内容: 简介 时钟 客户端 时区 客户端 绑定器与转换器 简介 虽然有些应用目标市场只是在一个时区,有些应用目标市场是许多不同时区,为满足这种需求并集中化日期操作,ABP为日期操作提供公 ...mvc5+ef6+Bootstrap 项目心得--身份验证…

jdbc连接gp单例模式_JDBC连接备忘单

jdbc连接gp单例模式抽象 这是常见数据库的JDBC连接的快速参考。 我似乎必须大量查找此信息&#xff0c;因此我认为最好将所有参考资料放在一个地方。 德比 <dependency><groupId>org.apache.derby</groupId><artifactId>derbyclient</artifactId&g…

关于 Mac OS X 内核技术来源

Mach&#xff08;Multiple Asynchronously Communication Hosts&#xff09; 是一个由卡内基梅隆大学开发的操作系统内核&#xff0c;Mach的开发是为了取代BSD的UNIX核心。 Mach 内核的设计目标之一是要兼容 Unix 系统。 当初他们的设想是&#xff0c;真正的操作系统可以作为一…

得到appd url_AppD方法:Java 9支持

得到appd url通过从您的应用程序学习企业APM产品&#xff0c;发现更快&#xff0c;更有效的性能监控。 参加AppDynamics APM导览&#xff01; 阅读有关Java 9模块化功能带来的挑战以及AppDynamics保持在该领域的领导者的严格要求的更多信息。 我们很高兴宣布Java 17全面支持&…

python有趣的面试题_python面试题目

问题一&#xff1a;以下的代码的输出将是什么? 说出你的答案并解释。 class Parent(object): x 1 class Child1(Parent): pass class Child2(Parent): pass print Parent.x, Child1.x, Child2.x Child1.x 2 print Parent.x, Child1.x, Child2.x Parent.x 3 print Parent.x,…

用C语言编程画出图形,C语言图形编程(六) -图形程序设计实例:零件图形的绘制...

实例&#xff1a;一个零件图形的绘制有一个零件图&#xff0c;如下&#xff1a;对图3-1中的零件图形&#xff0c;如何根据它所标注的尺寸&#xff0c;按照适当的顺序有步聚地画出该图形&#xff0c;这首先要分析此零件图形的几何关系&#xff0c;了解构成这个图形各线段的性质&…

Linux 发行版之 CentOS 简介

简介 CentOS&#xff08;Community Enterprise Operating System&#xff0c;社区企业操作系统&#xff09;是 Linux 发行版之一&#xff0c;它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码&#xff0c;因此有些要求高…

python异常值如何处理_如何处理异常

python异常值如何处理最近&#xff0c;我与一个朋友进行了讨论&#xff0c;他是一个相对初级但很聪明的软件开发人员。 她问我有关异常处理的问题。 这些问题指出了一种技巧和窍门&#xff0c;肯定有它们的清单。 但是我坚信我们编写软件的方式背后的背景和动机&#xff0c;因此…

c语言测试清单,c语言测试(C language test).doc

c语言测试(C language test)c语言测试(C language test)* * college course exam papersCourse Name: "C: the use of C language programming software to enter the world" (A) volumeGrade: class:Name: ______BUKAILI_________ number: _________________ test (…

mfc怎么获取进程的线程数_Python多线程获取小米应用商店App,看看我是怎么做到的

一、【项目背景】小米应用商店给用户发现最好的安卓应用和游戏,安全可靠&#xff0c;可是要下载东西要一个一个的搜索太麻烦了。而且速度并不是很快。今天小编就教大家利用多线程爬取小米应用商店的游戏模块&#xff0c;快速获取我们想要的软件安装包。二、【项目目标】目标 &a…

Linux Distribution Timeline for 2010(Linux 2010 年发行版时间线/族谱/发展图)

此图来自维基百科&#xff08;wikimedia&#xff09;&#xff0c;具体地址为&#xff1a;https://commons.wikimedia.org/wiki/File:Linux_Distribution_Timeline.svg?uselangzh-hans#filehistory

git强制推送_Git 常用命令

Git 常用命令总结1. GIT 工作区add commitworking directory ------- index(stage) ---------- HEAD | | | | | | 工作目录 暂存区 …

glacier2_Amazon Glacier的Scala客户端

glacier2Amazon Glacier是一项安全&#xff0c;耐用且成本极低的云存储服务&#xff0c;用于数据归档和长期备份。 Glacier提供了一种冷藏数据存档解决方案&#xff0c;这意味着已存储的数据不可立即检索。 您首先需要请求数据检索&#xff0c;访问时间可能从几分钟到几小时不等…

c 调用c语言dll数组,C#调用C类型dll入参为struct的问题详解

前言C# 可以通过 DllImport 的方式引用 C 类型的 dll。但很多 dll 的参数不会是简单的基础类型&#xff0c;而是结构体 struct 。因此就需要在 C# 端定义同样的结构体类型&#xff0c;才能实现调用 C 类型 dll。这里例举几种不同的结构体情况&#xff0c;以及其对应的解决方案。…

Slackware Linux 的发展历程

目前可供Linux用户使用的 发行版有很多种&#xff0c;它们虽基于共同的内核&#xff0c;但在安装、提供的应用程序、服务等方面各具特色&#xff0c;并拥有各自的用户群体。Slackware Linux是一个历史比较悠久的发行版&#xff0c;它的存在见证了Linux的发展历程&#xff0c;它…

golang 泛型_Golang 1.x版本泛型编程

本文介绍了Golang 1.x版本的泛型编程。往期回顾&#xff1a;浅谈动态追踪技术Go是一门天生为服务器程序设计的简洁的语言&#xff0c;因此Go的设计原则聚焦在可扩展性、可读性和并发性&#xff0c;而多态性并不是这门语言的设计初衷&#xff0c;因此就被放在了一边。虽然在2.0版…

jwt令牌_JWT令牌的秘密轮换

jwt令牌当您使用JSON Web令牌 &#xff08; JWT &#xff09;或需要对有效载荷信息进行签名或加密的任何其他令牌技术时&#xff0c;设置令牌的到期日期很重要&#xff0c;因此&#xff0c;如果令牌到期&#xff0c;则可以假定这可能被视为安全漏洞&#xff0c;您拒绝使用此令牌…

linux android build tools,build.gradle 文件中的 Android SDK Build Tools version

build.gradle 文件中的 Android SDK Build Tools versionAndroid,Gradle,SDK2018.07.17在 Android Gradle Plugin 3.0.1 中&#xff0c;最低的 Android SDK Build Tools 是 26.0.2&#xff0c;而我声明的 25.0.0 将被忽略掉。今天新建了一个 Android 项目的时候&#xff0c;无意…