On 11/02/2012 12:04 PM, Peter Levart wrote:
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 digress. Javadocs say: "The redefinition may change method bodies, the
constant pool and attributes. The redefinition must not add, remove or
rename fields or methods, change the signatures of methods, or change
inheritance. These restrictions maybe be lifted in future versions."
So currently there's no problem since redefinition can only change
method bodies, so reflecting over old or new version of the class
returns the same results.
But the synchronization bottleneck of initAnnotationsIfNecessary() could
be solved this way.
Regrads, Peter
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