On 11/02/2012 11:03 AM, Alexander Knöller wrote:
Hello there.

(Reposting this request for improvement as suggested in mailing list jdk6-dev)

java.lang.Class.getAnnotations() always enters a synchronized-block 
(initAnnotationsIfNecessary() ), slowing down multi core machines that heavily 
make use of Annotations.
(in our Case we use LoadTimeWeaving in the spring-framework 3.1.2)
We are using sun-jdk 6u27 on CentOS which has the same performance-bottleneck. 
I could not easily find sources for sunjdk (no open source anymore).
So I do not know if Versions 7 or 8 contain fixes for this.
openjdk7 and 8 show no fix so far, although it looks like it might be possible 
to build a kind of double-checked locking using local variables?


I am not very familiar with concurrency while using SoftReferences, but I guess 
using a local Variable for concurrency-avoidance for the annotations-field or 
the target of the SoftReference, then doing a nonsynchronized check for the 
redefinition and a potential null-value on the local copy of the 
annotations-variable should suffice to decide that one could leave out the 
synced call and just returns the annotations-value (referenced by the local 
variable)?
Also you would need to use a local variable while calculating the annotations 
prior to assigning the result to the annotations-field to avoid 
concurrency-effects on the double-checked locking.

Since Code using getAnnotations() always could get hit by an annotations-result not 
fitting any more to the class because of a concurrent thread redefining the class we 
would not need to take care of this (and the current code could cause unexpected 
behaviour already inside "getAnnotations()" after exiting the lock on 
initAnnotationsIfNecessary()).

Regards
Alex

Anfang der weitergeleiteten E-Mail:

Von: Alexander Knöller <alexander.knoel...@gmail.com>
Betreff: Re: bottleneck by java.lang.Class.getAnnotations()
Datum: 2. November 2012 08:43:06 MEZ
An: Joe Darcy <joe.da...@oracle.com>
Kopie: Alan Bateman <alan.bate...@oracle.com>, jdk6-...@openjdk.java.net

I know. But there is a usual solution to those kind of problems: double checked 
locking.
This would avoid synchronization for all the cases where no redefinitions take 
place.
(I also put in a bug-report for the sunjdk where I elaborated this a bit more.)
I am not so familiar with concurrency while using SoftReferences, but I guess 
using a local Variable for concurrency-avoidance for the annotations-field or 
the target of the SoftReference, then doing a nonsynchronized check for the 
redefinition and a potentially null-value on the local copy of the 
annotations-variable should suffice to decide that one could leave out the 
synced call and just returns the annotations-value (in the local variable)?

Also you would need to use a local variable while calculating the 
annotations-Field prior to assigning the result to the field to avoid 
concurrency-effects on the double-checked locking.

Since Code using getAnnotations() always could trap in an annotations-result not fitting 
any more to the class because of concurrent redefinition we would not need to take care 
of this (and the current code could cause this already inside 
"getAnnotations()".

-Alex

Am 01.11.2012 um 15:56 schrieb Joe Darcy:

On 11/1/2012 7:11 AM, Alan Bateman wrote:
On 01/11/2012 13:17, Alexander Knöller wrote:
Hi there.

java.lang.Class.getAnnotations() always enters a synchronized-block, slowing 
down multi core machines that heavily make use of Annotations.
(in our Case we use LoadTimeWeaving in the spring-framework 3.1.2)
We are using sun-jdk 6 which has the same performance-bottleneck.
openjdk7 and 8 show no fix so far, although it looks like it might be possible 
to build a kind of double-checked locking?

Has this issue ever been persued?

Special Regards
Alex Knöller

If you have a proposal then I suggest bringing it to core-libs-dev for 
discussion. In addition to contention there are other issues that need 
attention there too, particularly the potential to deadlock and the overhead 
per Class when annotations aren't used. There's definitely some useful work 
that could be done there.

Hi all,

initAnnotationsIfNecessary() is at least synchronized, so that it always gets the result right in face of concurrent calls to it and class redefinitions performed by VM, but other methods, such as for example:

    private Field[] privateGetDeclaredFields(boolean publicOnly)

...and many others are not. Therefore a theoretical race exists that could install an old version of fields into cached storage (declaredPublicFields or declaredFields in this case) that was taken before class was redefined and write it over the new version of fields...

I suggest redesigning the lazy construction / caching by employing versioned containers like the following:

static class VersionedSoftRef<T> extends SoftReference<T> {
  final int redefinedCount;
  VersionedSoftRef(T referent,  int redefinedCount) {
    super(referent);
    this.redefinedCount = redefinedCount;
  }
}

...to be used instead of plain SoftReferences in places like declaredPublicFields and such and using CAS (via Unsafe or AtomicReferenceFieldUpdater) to optimistically install lazily constructed data... The versioned containers would serve two purposes: each access to a part of cached data could be independently version-checked against current value of classRedefinedCount on a fast path before returning a cached value. In case of cache-miss (not data or stale data), a thread could compute the data concurrently with any other threads doing the same and using CAS at the end, install the latest version of data into cache.

For getAnnotations() I would use a similar technique (a versioned private subclass of HashMap for example).

If you like, I can prepare a patch and send it for review.


Regards, Peter



Note that the block in question is synchronized so that getAnnotations returns 
the right result if the class has been redefined at runtime using an API for 
that purpose.

-Joe


Reply via email to