bottleneck by java.lang.Class.getAnnotations() - rebased patch
Joe Darcy
joe.darcy at oracle.com
Mon Dec 10 20:52:57 UTC 2012
Yes; the OpenJDK sources only go back to circa 2007, which was part-way
into the JDK 7 release. The exact changesets of how annotations were
added back in JDK 5 are not available publicly. However, the final
results of that process may be mostly visible in the src.zip from JDK 5.
HTH,
-Joe
On 12/10/2012 8:13 AM, Peter Levart wrote:
> Hi David,
>
> It would be informative to look at how java.lang.Class evolved during
> history. The oldest revision I can access is from 1st of Dec. 2007,
> which already contains Java 1.5 code (annotations) and is more or less
> unchanged until now. The most interesting changesets would be those
> that introduced annotations.
>
> Regards, Peter
>
>
> On 12/10/2012 03:59 PM, Peter Levart wrote:
>> On 12/10/2012 07:18 AM, David Holmes wrote:
>>> Hi Peter,
>>>
>>> Sorry for the delay on this.
>>
>> Thank you for taking it into consideration. I can only imagine how
>> busy you are with other things.
>>
>>>
>>> Generally your VolatileData and my ReflectionHelper are doing a
>>> similar job. But I agree with your reasoning that all of the cached
>>> SoftReferences are likely to be cleared at once, and so a
>>> SoftReference to a helper object with direct references, is more
>>> effective than a direct reference to a helper object with
>>> SoftReferences. My initial stance with this was very conservative as
>>> the more change that is introduced the more uncertainty there is
>>> about the impact.
>>>
>>> I say the above primarily because I think, if I am to proceed with
>>> this, I will need to separate out the general reflection caching
>>> changes from the annotation changes. There are a number of reasons
>>> for this:
>>>
>>> First, I'm not at all familiar with the implementation of
>>> annotations at the VM or Java level, and the recent changes in this
>>> area just exacerbate my ignorance of the mechanics. So I don't feel
>>> qualified to evaluate that aspect.
>>>
>>> Second, the bulk of the reflection caching code is simplified by the
>>> fact that due to current constraints on class redefinition the
>>> caching is effectively idempotent for fields/methods/constructors.
>>> But that is not the case for annotations.
>>
>> I think that on the Class-level these two aspects can be separated.
>> But not on the Field/Method/Constructor level, because annotations
>> are referenced by Field/Method/Constructor objects and there is no
>> invalidation logic - like on the Class-level - that would just
>> invalidate the sets of annotations on Field/Method/Constructor,
>> leaving Field/Method/Constructor objects still valid. So these two
>> aspects are connected - it may be - I haven't looked at history yet -
>> that invalidation of Field/Method/Constructor was introduced just
>> because of annotations.
>>
>> Also, the following bug (currently not accessible):
>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5002251 has
>> references to docs that suggest that current class redefinition can
>> introduce new methods, so If this is correct, then redefinition is
>> not idempotent for basic reflection data.
>>
>>>
>>> Finally, the use of synchronization with the annotations method is
>>> perplexing me. I sent Joe a private email on this but I may as well
>>> raise it here - and I think you have alluded to this in your earlier
>>> emails as well: initAnnotationsIfNecessary() is a synchronized
>>> instance method but I can not find any other code in the VM that
>>> synchronizes on the Class object's monitor. So if this
>>> synchronization is trying to establish consistency in the face of
>>> class redefinition, I do not see where class redefinition is
>>> participating in the synchronization!
>>
>> I think that the intent was merely synchronized access to / lazy
>> initialization of cached 'annotations' and 'declaredAnnotations'
>> Maps. Field[], Method[], Constructor[] arrays are published to other
>> threads via volatile fields one field at a time, but
>> initAnnotationsIfNecessary() was designed to publish two references
>> ('annotations' and 'declaredAnnotations') atomically at the same
>> time, so the author of the code choose to use synchronized block. I
>> also have a feeling that this might have simply been the author's
>> preferred style of synchronization, since the same approach is used
>> also in Field/Method/Constructor/Executable although there's just one
>> field of annotations that is published at a time.
>>
>> It is indicative that initAnnotationsIfNecessary() synchronized
>> method also contains the call to clearCachesOnClassRedefinition(), so
>> at first it seems that the synchronization is also meant to serialize
>> access to invalidation logic which invalidates both
>> Field/Method/Constructor and annotation fields, but that all
>> falls-apart because clearCachesOnClassRedefinition() is also called
>> from elsewhere, not guarded by the synchronization.
>>
>> So all in all the two aspects - annotations and basic reflection
>> stuff - are quite intermingled in current code, unfortunately very
>> inconsistently. I'm afraid that doing one thing and not touching the
>> other is possible, but that would not solve the problems that this
>> thread was starting to address (bottleneck by
>> java.lang.Class.getAnnotations()) and evident dead-lock bugs.
>>
>> We can approach the problem so that we separate the aspects of
>> caching Class-level annotations and Field/Method/Constructor arrays
>> but using the same approach for both. And that would only make sense
>> if there was a reason to separately cache Class-level annotations and
>> Field/Method/Constructor arrays. I haven't yet been able to think of
>> one such reason. So anyone with more insight (the author of
>> annotations code?) is invited to participate in investigation.
>>
>> My approach of including the Class-level annotations together with
>> Field/Method/Constructor arrays was guided by:
>> - consistency - why should Class-level annotations be cached with
>> hard references, while Field/Method/Constructor annotations are
>> indirectly SoftReference(d)? Are they more important?
>> - simplicity
>> - space efficiency
>> - correctness - unsynchronized calls to
>> clearCachesOnClassRedefinition() followed by unsynchronized lazy
>> initialization have - as I have shown - theoretical races that could
>> result in caching the old versions of data instead of new. The
>> approach I have chosen with the logic in getVolatileData() is a kind
>> of MVCC rather than synchronization.
>>
>> But as said, the two aspects of caching can be separated.
>>
>> We can also leave the Class-level annotation aspect untouched by
>> re-introducing the 'annotations' and 'declaredAnnotations' fields and
>> also the 'lastRedefinedCount' field on the Class-level and
>> re-introducing the synchronized initAnnotationsIfNecessary() method
>> and a clearCachesOnClassRedefinition() which would just invalidate
>> the two annotations fields.
>>
>> To recap. How to continue?
>> a) as proposed;
>> b) separate caching of annotations and Field/Method/Constructor
>> arrays but with the same unblocking MVCC-like approach for both -
>> with possible variation in that annotations be hardly referenced and
>> Field/Method/Constructor arrays be softly;
>> c) undo the annotations caching changes and only do MVCC for
>> Field/Method/Constructor arrays.
>>
>> I can do b) but prepare two independent patches - one for
>> VolatileData (rename it to MemberData?) and one for AnnotationData.
>> So by applying only the first, we achieve c) and can later apply the
>> second to upgrade to b). But it is unfortunately a) that is most
>> efficient space-saving wise.
>>
>> What do you say about the trivial changes in
>> Field/Method/Constructor/Executable to accommodate caching on the
>> 'root' instance instead of on the copies?
>>
>> Regards, Peter
>>
>>>
>>> So what I would like to do is take your basic VolatileData part of
>>> the patch and run with it for JEP-149 purposes, while separating the
>>> annotations issue so they can be dealt with by the experts in that
>>> particular area.
>>>
>>> I'm sorry it has taken so long to arrive at a fairly negative
>>> position, but I need someone else to take up the annotations
>>> gauntlet and run with it.
>>>
>>> Thanks,
>>> David
>>>
>>> On 3/12/2012 5:41 PM, Peter Levart wrote:
>>>> Hi David, Alan, Alexander and others,
>>>>
>>>> In the meanwhile I have added another annotations space
>>>> optimization to
>>>> the patch. If a Class doesn't inherit any annotations from a
>>>> superclass,
>>>> which I think is a common case, it assigns the same Map instance to
>>>> "annotations" as well as "declaredAnnotations" fields. Previously -
>>>> and
>>>> in the original code - this only happened for java.lang.Object and
>>>> interfaces.
>>>>
>>>> Here's the updated webrev:
>>>>
>>>> http://dl.dropbox.com/u/101777488/jdk8-tl/JEP-149/webrev.02/index.html
>>>>
>>>> I have also rewritten the performance micro-benchmarks. With the
>>>> addition of repeating annotations, one performance aspect surfaces:
>>>> when
>>>> asking for a particular annotation type on a Class and that annotation
>>>> is not present, the new repeating annotations support method
>>>> AnnotationSupport.getOneAnnotation asks for @ContainedBy
>>>> meta-annotation
>>>> on the annotation type. This can result in an even more apparent
>>>> synchronization hot-spot with original code that uses synchronized
>>>> initAnnotationsIfNecessary(). This aspect is tested with the 3rd test.
>>>> Other 2 tests test the same thing as before but are more stable now,
>>>> since now they measure retrieval of 5 different annotation types from
>>>> each AnnotatedElement, previously they only measured retrieval of 1
>>>> which was very sensitive to HashMap irregularities (it could happen
>>>> that
>>>> a particular key mapped to a bucket that was overloaded in one
>>>> test-run
>>>> and not in another)...
>>>>
>>>> Here're the new tests:
>>>>
>>>> https://raw.github.com/plevart/jdk8-tl/JEP-149/test/src/test/ReflectionTest.java
>>>>
>>>>
>>>> And the corresponding results when run on an i7 CPU on Linux:
>>>>
>>>> https://raw.github.com/plevart/jdk8-tl/JEP-149/test/benchmark_results_i7-2600K.txt
>>>>
>>>>
>>>>
>>>> Regards, Peter
>>>>
>>>>
>>>>
>>>> On 12/03/2012 02:15 AM, David Holmes wrote:
>>>>> On 1/12/2012 4:54 AM, Alan Bateman wrote:
>>>>>> On 30/11/2012 18:36, Peter Levart wrote:
>>>>>>> :
>>>>>>>
>>>>>>> So, what do you think? What kind of tests should I prepare in
>>>>>>> addidion
>>>>>>> to those 3 so that the patch might get a consideration?
>>>>>> I think this is good work and thanks for re-basing your patch. I
>>>>>> know
>>>>>> David plans to do a detail review. I think it will require extensive
>>>>>> performance testing too, perhaps with some large applications.
>>>>>
>>>>> Indeed I do plan a detailed review and have initiated some initial
>>>>> performance tests.
>>>>>
>>>>> I am also swamped but will try to get to the review this week - and
>>>>> will also need to check the referenced annotations updates.
>>>>>
>>>>> David
>>>>>
>>>>>> -Alan
>>>>>>
>>>>
>>
>
More information about the core-libs-dev
mailing list