日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java反射,但速度更快

發(fā)布時(shí)間:2023/12/3 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java反射,但速度更快 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在編譯時(shí)不知道Java類的最快方法是什么? Java框架通常會(huì)這樣做。 很多。 它可以直接影響其性能。 因此,讓我們對(duì)不同的方法進(jìn)行基準(zhǔn)測(cè)試,例如反射,方法句柄和代碼生成。

用例

假設(shè)我們有一個(gè)簡(jiǎn)單的Person類,其中包含名稱和地址:

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

并且我們想使用諸如以下的框架:

  • XStream ,JAXB或Jackson來(lái)將實(shí)例序列化為XML或JSON。
  • JPA /休眠將人員存儲(chǔ)在數(shù)據(jù)庫(kù)中。
  • OptaPlanner分配地址(如果他們是游客或無(wú)家可歸的人)。

這些框架都不了解Person類。 因此,他們不能簡(jiǎn)單地調(diào)用person.getName() :

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

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

但是這樣的代碼被稱為很多 :

  • 如果在數(shù)據(jù)庫(kù)中插入1000個(gè)不同的人,則JPA / Hibernate可能會(huì)調(diào)用2000次這樣的代碼:
    • 1000次調(diào)用Person.getName()
  • 同樣,如果您用XML或JSON編寫(xiě)1000個(gè)不同的人,則XStream,JAXB或Jackson可能會(huì)進(jìn)行2000次調(diào)用。

顯然,當(dāng)這種代碼每秒被調(diào)用x次時(shí), 其性能很重要

基準(zhǔn)測(cè)試

使用JMH,我在帶有32GB RAM的64位8核Intel i7-4790臺(tái)式機(jī)上的Linux上使用OpenJDK 1.8.0_111運(yùn)行了一組微型基準(zhǔn)測(cè)試。 JMH基準(zhǔn)測(cè)試有3個(gè)分支,5個(gè)1秒的預(yù)熱迭代和1秒的20個(gè)測(cè)量迭代。

該基準(zhǔn)的源代碼位于此GitHub存儲(chǔ)庫(kù)中 。

TL; DR結(jié)果

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

(*)在用例中,我以使用的工作量作為基準(zhǔn)。 你的旅費(fèi)可能會(huì)改變。

因此,魔鬼在細(xì)節(jié)中。 讓我們?yōu)g覽一下實(shí)現(xiàn),以確認(rèn)我應(yīng)用了典型的魔術(shù)技巧(例如setAccessible(true) )。

實(shí)作

直接訪問(wèn)(基準(zhǔn))

我使用了一個(gè)普通的person.getName()調(diào)用作為基準(zhǔn):

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

直接訪問(wèn)自然是運(yùn)行時(shí)最快的方法,而沒(méi)有引導(dǎo)成本。 但是它在編譯時(shí)導(dǎo)入Person ,因此每個(gè)框架都無(wú)法使用它。

反射

框架在運(yùn)行時(shí)讀取getter的明顯方法是不預(yù)先知道它的方法是通過(guò)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)調(diào)用可使這些反射調(diào)用更快,但是即使這樣,每個(gè)調(diào)用也要花費(fèi)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

反射比直接訪問(wèn)慢106%(大約慢一倍)。 預(yù)熱還需要更長(zhǎng)的時(shí)間。

這對(duì)我來(lái)說(shuō)不是什么大驚喜,因?yàn)楫?dāng)我使用OptaPlanner在980個(gè)城市中描述(使用抽樣)一個(gè)人為簡(jiǎn)單的旅行商問(wèn)題時(shí),反射成本像拇指酸痛一樣突出:

方法句柄

Java 7中引入了MethodHandle來(lái)支持invokedynamic指令。 根據(jù)javadoc,它是對(duì)基礎(chǔ)方法的類型化,直接可執(zhí)行的引用。 聽(tīng)起來(lái)很快,對(duì)不對(duì)?

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中的反射還要慢 。它每次操作花費(fèi)6.1納秒,因此比直接訪問(wèn)慢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在靜態(tài)字段中,則每次操作只需要5.5納秒,這仍然與反射一樣慢 。 此外,對(duì)于大多數(shù)框架而言,這是無(wú)法使用的。 例如,JPA實(shí)現(xiàn)可能需要反映n類( Person , Company , Order等等)的m getters( getName() , getAddress() , getBirthDate() ,...),因此JPA實(shí)現(xiàn)如何有n * m靜態(tài)字段,在編譯時(shí)不知道n或m ?

我確實(shí)希望MethodHandle在將來(lái)的Java版本中能夠像直接訪問(wèn)一樣快,從而取代對(duì)...的需求。

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

在Java中,可以在運(yùn)行時(shí)編譯和運(yùn)行生成的Java代碼。 因此,使用javax.tools.JavaCompiler API,我們可以在運(yùn)行時(shí)生成直接訪問(wèn)代碼:

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);}

有關(guān)如何使用javax.tools.JavaCompiler更多信息,請(qǐng)參見(jiàn)本文或本文的 第2頁(yè) 。 除了javax.tools之外,類似的方法也可以使用ASM或CGLIB,但是這些方法會(huì)推斷出額外的依賴性,并且可能會(huì)產(chǎn)生不同的性能結(jié)果。

無(wú)論如何, 生成的代碼與直接訪問(wèn)一樣快

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

因此,當(dāng)我再次在OptaPlanner中運(yùn)行該完全相同的Traveling Salesman問(wèn)題時(shí),這一次使用代碼生成來(lái)訪問(wèn)計(jì)劃變量, 因此總分計(jì)算速度提高了18% 。 并且分析(使用采樣)看起來(lái)也更好:

請(qǐng)注意,在正常使用情況下,由于大量CPU需要實(shí)際復(fù)雜的分?jǐn)?shù)計(jì)算,因此性能提升幾乎是無(wú)法檢測(cè)到的...

運(yùn)行時(shí)代碼生成的唯一缺點(diǎn)是,它會(huì)導(dǎo)致可觀的引導(dǎo)成本,特別是如果生成的代碼未進(jìn)行批量編譯時(shí)。 因此,我仍然希望有一天MethodHandles能夠像直接訪問(wèn)一樣快,只是為了避免增加引導(dǎo)成本。

結(jié)論

在此基準(zhǔn)測(cè)試中,反射和MethodHandles的速度是OpenJDK 8中直接訪問(wèn)的兩倍,但是生成的代碼的速度是直接訪問(wèn)的速度。

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

總結(jié)

以上是生活随笔為你收集整理的Java反射,但速度更快的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。