bottleneck by java.lang.Class.getAnnotations() - proposed patch
Peter Levart
peter.levart at gmail.com
Sun Nov 4 23:09:36 UTC 2012
Hi,
I propose the following patch to java.lang.Class in order to overcome
the synchronization bottleneck when accessing annotations from multiple
threads. The patch packs the 'annotations' and 'declaredAnnotations'
Maps together with an int redefinedCount into a structure:
static class Annotations {
final Map<Class<? extends Annotation>, Annotation> annotations;
final Map<Class<? extends Annotation>, Annotation>
declaredAnnotations;
final int redefinedCount;
which is referenced by a single volatile Class.annotations field.
Instead of initAnnotationsIfNecessary() there's a
getAnnotationsPrivate() method that uses simple double-checked locking
to lazily initialize and return this structure. The slow-path part is
still synchronized to ensure that
Class.setAnnotationType/getAnnotationType call backs from AnnotationType
are only done while holding a lock.
Regards, Peter
Here's the patch to jdk8 source:
diff -r 7ac292e57b5a src/share/classes/java/lang/Class.java
--- a/src/share/classes/java/lang/Class.java Thu Nov 01 14:12:21 2012
-0700
+++ b/src/share/classes/java/lang/Class.java Sun Nov 04 23:53:04 2012
+0100
@@ -2237,10 +2237,8 @@
declaredFields = publicFields = declaredPublicFields = null;
declaredMethods = publicMethods = declaredPublicMethods =
null;
declaredConstructors = publicConstructors = null;
- annotations = declaredAnnotations = null;
- // Use of "volatile" (and synchronization by caller in the case
- // of annotations) ensures that no thread sees the update to
+ // Use of "volatile" ensures that no thread sees the update to
// lastRedefinedCount before seeing the caches cleared.
// We do not guard against brief windows during which multiple
// threads might redundantly work to fill an empty cache.
@@ -3049,8 +3047,7 @@
if (annotationClass == null)
throw new NullPointerException();
- initAnnotationsIfNecessary();
- return (A) annotations.get(annotationClass);
+ return (A)
getAnnotationsPrivate().annotations.get(annotationClass);
}
/**
@@ -3070,40 +3067,52 @@
* @since 1.5
*/
public Annotation[] getAnnotations() {
- initAnnotationsIfNecessary();
- return AnnotationParser.toArray(annotations);
+ return
AnnotationParser.toArray(getAnnotationsPrivate().annotations);
}
/**
* @since 1.5
*/
public Annotation[] getDeclaredAnnotations() {
- initAnnotationsIfNecessary();
- return AnnotationParser.toArray(declaredAnnotations);
+ return
AnnotationParser.toArray(getAnnotationsPrivate().declaredAnnotations);
}
// Annotations cache
- private transient Map<Class<? extends Annotation>, Annotation>
annotations;
- private transient Map<Class<? extends Annotation>, Annotation>
declaredAnnotations;
+ private transient volatile Annotations annotations;
- private synchronized void initAnnotationsIfNecessary() {
- clearCachesOnClassRedefinition();
- if (annotations != null)
- return;
- declaredAnnotations = AnnotationParser.parseAnnotations(
- getRawAnnotations(), getConstantPool(), this);
- Class<?> superClass = getSuperclass();
- if (superClass == null) {
- annotations = declaredAnnotations;
- } else {
- annotations = new HashMap<>();
- superClass.initAnnotationsIfNecessary();
- for (Map.Entry<Class<? extends Annotation>, Annotation> e :
superClass.annotations.entrySet()) {
- Class<? extends Annotation> annotationClass = e.getKey();
- if
(AnnotationType.getInstance(annotationClass).isInherited())
- annotations.put(annotationClass, e.getValue());
+ private Annotations getAnnotationsPrivate() {
+ // double checked locking
+ int redefinedCount = classRedefinedCount;
+ Annotations anns = annotations;
+ if (anns != null && anns.redefinedCount == redefinedCount) {
+ return anns;
+ }
+
+ synchronized (this) {
+ redefinedCount = classRedefinedCount;
+ anns = annotations;
+ if (anns != null && anns.redefinedCount == redefinedCount) {
+ return anns;
}
- annotations.putAll(declaredAnnotations);
+
+ Map<Class<? extends Annotation>, Annotation> declAnnMap =
AnnotationParser.parseAnnotations(
+ getRawAnnotations(), getConstantPool(), this);
+ Map<Class<? extends Annotation>, Annotation> annMap;
+ Class<?> superClass = getSuperclass();
+ if (superClass == null) {
+ annMap = declAnnMap;
+ } else {
+ annMap = new HashMap<>();
+ for (Map.Entry<Class<? extends Annotation>, Annotation>
e : superClass.getAnnotationsPrivate().annotations.entrySet()) {
+ Class<? extends Annotation> annotationClass =
e.getKey();
+ if
(AnnotationType.getInstance(annotationClass).isInherited())
+ annMap.put(annotationClass, e.getValue());
+ }
+ annMap.putAll(declAnnMap);
+ }
+
+ annotations = anns = new Annotations(annMap, declAnnMap,
redefinedCount);
+ return anns;
}
}
@@ -3119,6 +3128,18 @@
return annotationType;
}
+ static final class Annotations {
+ final Map<Class<? extends Annotation>, Annotation> annotations;
+ final Map<Class<? extends Annotation>, Annotation>
declaredAnnotations;
+ final int redefinedCount;
+
+ Annotations(Map<Class<? extends Annotation>, Annotation>
annotations, Map<Class<? extends Annotation>, Annotation>
declaredAnnotations, int redefinedCount) {
+ this.annotations = annotations;
+ this.declaredAnnotations = declaredAnnotations;
+ this.redefinedCount = redefinedCount;
+ }
+ }
+
/* Backing store of user-defined values pertaining to this class.
* Maintained by the ClassValue class.
*/
More information about the core-libs-dev
mailing list