JDK-6407335 : (ann) java.lang.Class.getAnnotation() cache conflicts with RedefineClasses()
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2006-04-01
  • Updated: 2010-05-10
  • Resolved: 2006-05-30
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6
5.0u8Fixed 6 b86Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
java.lang.Class.getAnnotation() is currently implemented with a cache:

    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
        if (annotationClass == null)
            throw new NullPointerException();

        initAnnotationsIfNecessary();
        return (A) annotations.get(annotationClass);
    }

<snip>

    private synchronized void initAnnotationsIfNecessary() {
        if (annotations != null)
            return;
        declaredAnnotations = AnnotationParser.parseAnnotations(
            getRawAnnotations(), getConstantPool(), this);
        Class<?> superClass = getSuperclass();
        if (superClass == null) {
            annotations = declaredAnnotations;
        } else {
            annotations = new HashMap<Class, Annotation>();
            superClass.initAnnotationsIfNecessary();
            for (Map.Entry<Class, Annotation> e : superClass.annotations.entrySet()) {
                Class annotationClass = e.getKey();
                if (AnnotationType.getInstance(annotationClass).isInherited())
                    annotations.put(annotationClass, e.getValue());
            }
            annotations.putAll(declaredAnnotations);
        }
    }

This cache means that getRawAnnotations() is only called once
which doesn't interact well with JVM/TI RedefineClasses(). If
a set of annotations is modified by RedefineClasses(), then
the VM will return the modified annotation (once 5002251 is
fixed), but the Java layer will never present the modified
annotations to the caller.

Comments
EVALUATION Class redefinition has been expanded to allow for the addition of new methods, and the removal of some existing ones. Although other elements like fields may not currently be added, we should prepare for the possibility of this in a future release. So in addition to clearing the annotations cache on class redefinition, the following caches should be cleared as well: declaredFields publicFields declaredMethods publicMethods declaredConstructors publicConstructors declaredPublicFields declaredPublicMethods
08-05-2006

SUGGESTED FIX Another option is to disable the cache when the "can redefine classes" capability is enabled. This capability is not enabled by default and only agents that are interested in RedefineClasses() enable it. The issue about inherited annotations is a little more complex. I understand that a "class b" that subclasses from "class a" can also inherit annotations from "class a", but I'm not sure that a redefine of "class a"'s annotations should automatically propogate to "class b". I can see arguments both ways. In particular, if "class b" overrides a method from "class a" what does that mean for an annotation on "class a"'s version of the method?
04-04-2006

SUGGESTED FIX Add to the complications that it might be a super class that was redefined. So perhaps the version number should also be updated for subclasses when a class is redefined.
04-04-2006

SUGGESTED FIX I think 3) is the best alternative. Creating the annotation is fairly expensive; I don't think it should be redone every time. Perhaps a simple int version number that could be queried separately from the annotation data would suffice.
01-04-2006

SUGGESTED FIX I see a few options: 1) remove the cache 2) add a public API to flush the cache (doesn't help existing code) 3) add some private API where getAnnotation() can determine if the underlying cache has been redefined since the cache was created
01-04-2006