Java 多次调用Method/Field.getAnnotation(类)的性能,而不是在映射中预缓存此数据

Java 多次调用Method/Field.getAnnotation(类)的性能,而不是在映射中预缓存此数据,java,performance,reflection,map,annotations,Java,Performance,Reflection,Map,Annotations,我想知道(在Java中)重复调用方法.getAnnotation(Class)和字段.getAnnotation(Class)方法与(在程序启动时)存储带有类的元数据信息的预计算映射并在以后重复查询的性能是否有任何比较/研究。哪一个可以提供最好的运行时性能 在Java5、6和7下的性能是相同的?我想这在一定程度上取决于JVM实现。但以Oracle JVM为例,它维护了方法和字段实例上所有注释的缓存,这与您提到的映射方法相当 但这里有一个陷阱;由于方法/字段实例对于每个对象都是唯一的,如果您最终为

我想知道(在Java中)重复调用
方法.getAnnotation(Class)
字段.getAnnotation(Class)
方法与(在程序启动时)存储带有类的元数据信息的预计算映射并在以后重复查询的性能是否有任何比较/研究。哪一个可以提供最好的运行时性能


在Java5、6和7下的性能是相同的?

我想这在一定程度上取决于JVM实现。但以Oracle JVM为例,它维护了方法和字段实例上所有注释的缓存,这与您提到的映射方法相当

但这里有一个陷阱;由于方法/字段实例对于每个对象都是唯一的,如果您最终为给定的类创建了许多对象,那么您几乎失去了提供的性能优势。在这种情况下,类名+方法名/类名+字段名到相关注释列表的静态映射胜过使用的内置缓存方法

顺便问一下,你是如何预先计算地图的?它是在应用程序启动时完成的还是自动生成的代码?您是否确实确认在您的案例中缓存注释实例是安全的


与往常一样,对于此类问题,最好的解决方案是在线使用你的应用程序进行评测/测量,然后在给定的用例中使用看起来像是赢的解决方案。

地图应该是更可取的方法。主要问题不仅仅是缓存。同时也改善了多线程争用。在方法.getAnnotation()中,它调用一个同步的私有方法declaredAnnotations()。同步方法对高线程应用程序有不良影响。

我知道这是一个相当老的问题,但对于较新的JDK,结果可能仍然令人感兴趣

我编写了一些JMH基准测试,以了解注释信息的缓存可能产生的影响:

@状态(Scope.Thread)
公共静态类StateWithMethodAndHashMap{
public StateWithMethodAndHashMap(){
试一试{
cache=newhashmap();
method=AnnotationCachingBenchmark.class.getMethod(“methodWithAnnotations”);
final Annotation=method.getAnnotation(弃用的.class);
cache.put(方法、注释);
}捕获(例外e){
抛出新的运行时异常(e);
}
}
最终HashMap缓存;
最终方法;
}
@状态(Scope.Thread)
公共静态类StateWithMethodAndConcurrentHashMap{
public StateWithMethodAndConcurrentHashMap()的公共状态{
试一试{
cache=新的ConcurrentHashMap();
method=AnnotationCachingBenchmark.class.getMethod(“methodWithAnnotations”);
cache.put(方法,方法.getAnnotation(弃用的.class));
}捕获(例外e){
抛出新的运行时异常(e);
}
}
最终ConcurrentHashMap缓存;
最终方法;
}
@状态(Scope.Thread)
公共静态类StateWithMethod{
public StateWithMethod(){
试一试{
method=AnnotationCachingBenchmark.class.getMethod(“methodWithAnnotations”);
}捕获(例外e){
抛出新的运行时异常(e);
}
}
最终方法;
}
@不赞成
公共void方法WithAnnotations(){
}
@基准
@基准模式(Mode.All)
@输出时间单位(时间单位纳秒)
public void annotationsByReflection(final Blackhole aBh,final StateWithMethod aState)引发异常{
aBh.consume(aState.method.isAnnotationPresent(弃用的.class)
||aState.method.getClass().isAnnotationPresent(弃用的.class));
}
@基准
@基准模式(Mode.All)
@输出时间单位(时间单位纳秒)
public void annotationsByHashMap(final Blackhole aBh,final statewithmethod和hashmap aState)引发异常{
消耗(aState.cache.get(aState.method));
}
@基准
@基准模式(Mode.All)
@输出时间单位(时间单位纳秒)
公共无效注释ByConcurrentHashMap(最终黑洞aBh、最终状态WithMethod和ConcurrentHashMap aState)
抛出异常{
消耗(aState.cache.get(aState.method));
}
JDK 1.8.0_172,Java热点(TM)64位服务器虚拟机,25.172-b11:

基准模式Cnt分数错误单位
注释cachingbenchmark.annotationsbycurrenthashmap thrpt 5 0.152±0.009 ops/ns
注释CachingBenchmark.annotationsByHashMap thrpt 5 0.144±0.005 ops/ns
注释CachingBenchmark.annotationsByReflection thrpt 5 0.043±0.001 ops/ns
注释CachingBenchmark.annotationsByConcurrentHashMap avgt 5 6.610±0.094 ns/op
注释CachingBenchmark.annotationsByHashMap平均值5 6.963±0.414纳秒/升
注释CachingBenchmark.annotationsByReflection avgt 5 23.248±0.339 ns/op
JDK 13,OpenJDK 64位服务器虚拟机,13+33:

基准模式Cnt分数错误单位
注释cachingbenchmark.annotationsbycurrenthashmap thrpt 5 0.128±0.027 ops/ns
注释CachingBenchmark.annotationsByHashMap thrpt 5 0.136±0.031 ops/ns
注释CachingBenchmark.annotationsByReflection thrpt 5 0.139±0.010 ops/ns
注释CachingBenchmark.annotationsByConcurrentHashMap avgt 5 7.335±1.067 ns/op
注释CachingBenchmark.annotationsByHashMap平均值5 6.634±0.184纳秒/升
注释CachingBenchmark.annotationsByReflection avgt 5 7.234±0.567 ns/op
通过比较1.8和13,您可以清楚地看到JDK缓存注释的效果。所以