ReflectionData space optimization in java.lang.Class (JEP-149)
Peter Levart
peter.levart at gmail.com
Thu Dec 13 10:13:26 UTC 2012
Hi Mandy, David and others,
Here's the updated version of the patch for ReflectionData:
http://dl.dropbox.com/u/101777488/jdk8-tl/JEP-149.c/webrev.02/index.html
I split long lines as proposed by Mandy. I also split reflectionData()
method in two methods, so the fast-path method is smaller and might get
better chances of being in-lined.
I also found code-paths that evaluated reflectionData() method more than
once for each external call. It's the methods:
private Field[] privateGetDeclaredFields(boolean publicOnly)
and
private Method[] privateGetDeclaredMethods(boolean publicOnly)
which are called from:
private Field[] privateGetPublicFields(Set<Class<?>> traversedInterfaces)
and
private Method[] privateGetPublicMethods()
respectively. I therefore introduced overloaded variants of the former
methods taking a ReflectionData parameter like the following:
private Field[] privateGetDeclaredFields(boolean publicOnly) {
return privateGetDeclaredFields(publicOnly, reflectionData());
}
// A variant called from methods that already obtained
ReflectionData instance
private Field[] privateGetDeclaredFields(boolean publicOnly,
ReflectionData<T> rd) {
...
the same for privateGetDeclaredMethods. This is not for performance
reasons (though it might help) but for correctness. Each external call
should be a separate "transaction" and should work on the same
ReflectionData instance. The "transaction" is only guaranteed on the
level of a particular java.lang.Class instance though. Some methods also
invoke other Class instances (to gather inherited public methods /
fields) and those invocations might be separate transactions in the face
of concurrent class re-definitions. But we are not going to implement a
database here, are we?
I prepared some micro benchmarks for individual public methods. Here're
the results:
https://raw.github.com/plevart/jdk8-tl/JEP-149.c/test/reflection_data_benchmark_results_i7-2600K.txt
they indicate no noticeable performance decreases. Some methods are in
fact faster (more in-linable?):
- getFields - 4x
- getDeclaredConstructor - 4x ... 10x
- getDeclaredMethod - 3x
Here's the code for micro-benchmarks:
https://raw.github.com/plevart/jdk8-tl/JEP-149.c/test/src/test/ReflectionDataTest.java
Regards, Peter
On 12/12/2012 11:59 PM, Mandy Chung wrote:
> Hi Peter,
>
> On 12/12/12 4:31 AM, Peter Levart wrote:
>> Hi all,
>>
>> Ok, no problem. So here's a patch that summarizes what I did in the
>> previous patch, but only for reflective fields (Field[], Method[],
>> Constructor[]) not including annotations:
>>
>> http://dl.dropbox.com/u/101777488/jdk8-tl/JEP-149.c/webrev/index.html
>>
>
> Finally able to make time to review this patch. It's good work. While
> it's good to see the synchronization issue with annotations be fixed,
> separating the cache for reflection and annotation helps. As David
> replied, he will take your patch and run with it for JEP-149.
>
> The change looks good. Nit: there are several long lines
> L2235,2244,2245,2249,2269 etc that should be broken into separate
> lines. The remaining open question is the performance difference in
> the reflectionData() method and how well it will be jit'ed. In the
> common case, there is no class redefinition where
> classCachesOnClassRedefinition() is essentially like an nop. I
> believe David will look at the footprint and performance numbers as he
> has initiated the performance testing (need to do it with this new
> patch).
>
> Thanks
> Mandy
>
>> The annotations part is unchanged semantically, but I had to:
>>
>> - modify private method clearCachesOnClassRedefinition to only
>> include invalidation of annotations and declaredAnnotations fields. I
>> also renamed it to clearAnnotationCachesOnClassRedefinition
>> - renamed lastRedefinedCount to lastAnnotationsRedefinedCount and,
>> since this field is now only accessed while holding a lock (from
>> synchronized initAnnotationsIfNecessary), I removed the volatile
>> keyword.
>>
>> That's it.
>>
>> While looking at this unchanged part of code some more, I see other
>> races as well. For example: two concurrent accesses to annotations
>> combined with redefinition of a class can result in NPE. Here's a
>> serial execution:
>>
>> Thread 1:
>> getAnnotation(annotationType);
>> initAnnotationsIfNecessary();
>>
>> VM:
>> classRedefinedCount++;
>>
>> Thread 2:
>> getAnnotation(annotationType);
>> initAnnotationsIfNecessary();
>> clearAnnotationCachesOnClassRedefinition();
>> annotations = null;
>>
>> Thread 1:
>> return AnnotationSupport.getOneAnnotation(annotations,
>> annotationClass);
>> // 'annotations' field can be null
>>
>>
>> So this needs to be fixed sooner or later.
>>
>> Joel!
>>
>> Are your JSR 308 canges involving java.lang.Class too?
>>
>> Regards, Peter
>>
>>
>> On 12/12/2012 11:59 AM, Joel Borggrén-Franck wrote:
>>> Hi all,
>>>
>>> First, thanks all of you that are involved in this!
>>>
>>> I agree with David here, lets split this up (for now) and do
>>> reflective objects in the context of jep-149 and annotations
>>> separately.
>>>
>>> As you may know there are even more annotation coming in with JSR
>>> 308 annotations on type (use), so I want to complete that work first
>>> and then do the effort of reducing contention and overhead for both
>>> type and regular annotations and also fixing up the behaviour for
>>> redefine/retransform class.
>>>
>>> One other point to consider is that some of the fields in
>>> java/lang/reflect/ classes are known by the VM so not all changes in
>>> Java-land are actually doable. Glancing over your patches very
>>> quickly I don't think you have done anything to upset the VM, but
>>> then I am not an expert in this area.
>>>
>>> Also, with the VM permgen changes we might have to rethink our
>>> assumptions in order to reduce total overhead. For example as I
>>> understand it previously we could just ship the same pointer into
>>> permgen for the annotations arrays, now that isn't possible so we
>>> create a new copy of the array for every Field/Method/Constructor
>>> instance. Perhaps there is some clever way of eliminating those copies.
>>>
>>> So while I think your patches generally makes sense, I think it is
>>> prudent to delay this for annotations until all our new annotation
>>> features are in.
>>>
>>> cheers
>>> /Joel
>>>
>>> On Dec 10, 2012, at 7:18 AM, David Holmes <david.holmes at oracle.com>
>>> wrote:
>>>
>>>> Hi Peter,
>>>>
>>>> Sorry for the delay on this.
>>>>
>>>> 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.
>>>>
>>>> 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!
>>>>
>>>> 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
>>>>
More information about the core-libs-dev
mailing list