RFR: 8029019: (ann) Optimize annotation handling in core reflection
Claes Redestad
claes.redestad at oracle.com
Tue Jan 31 13:43:42 UTC 2017
Hi Peter,
On 01/25/2017 08:01 PM, Peter Levart wrote:
> Hi Claes,
>
> On 01/24/2017 03:34 PM, Claes Redestad wrote:
>> Hi Peter,
>>
>> thanks for the thorough examination of this issue.
>
> Thanks for looking into this change...
>
>> After going through
>> the patch I do like what I see, but I have a few comments:
>>
>> AnnotationInvocationHandler:
>> in invoke, cleaning up and replacing the explicit AssertionError should
>> be fine, but perhaps convert it to an assert that the number of
>> arguments is 1 when methodName is "equals" and 0 otherwise?
>
> In the following part of code:
>
> public Object invoke(Object proxy, Method method, Object[] args) {
>
> String memberName = method.getName(); // guaranteed interned
> String
> Class<?> dtype = method.getDeclaringClass();
>
> if (dtype == Object.class || // equals/hashCode/toString
> dtype == Annotation.class // annotationType
> ) {
> if (memberName == "equals") return equalsImpl(proxy,
> args[0]);
> if (memberName == "hashCode") return hashCodeImpl();
> if (memberName == "annotationType") return type;
> if (memberName == "toString") return toStringImpl();
> throw new AssertionError("Invalid method: " + method);
> }
>
> ...in the if statement, when method's declaring class is either Object
> or Annotation, we have just a limited number of methods that are
> possible candidates and we can identify them just by their names
> (Method::getName returns interned string, so identity comparison is
> possible). For example: if method's name is "equals" and declaring
> class is either Object or Annotation, then we know that this method
> has a signature of Object::equals - we don't need to check - this is
> part of language spec.
>
> The "throw new AssertionError("Invalid method: " + method)" is an
> unreachable statement until some new method is added to either Object
> or Annotation and at that time, if it ever comes to that, this code
> should fail and should be revised...
>
> The assert you are talking about is meaningful only if inserted as 1st
> statement inside the if statement. Will add it.
Yes, thanks!
>
>>
>> Adding @Stable is fine if that has a performance impact, but I think
>> we might preserve clarity of intent if you kept the fields as final and
>> kept using Unsafe to deserialize properly.
>
> Ok, will make them final but keep @Stable.
Good.
>
>>
>> AnnotationSupport:
>> I dislike that this code was already catching Throwable, and that
>> you're now copying that approach. Could the new the catch clause mimic
>> the one that previously wrapped the entire method?
>
> I think we can do it even better than that. See new webrev...
Oh, right, since we're avoiding InvocationHandler::invoke we don't have to
catch Throwable here.
Tangent: could we theoretically respecify InvocationHandler to throw
Exception
rather than Throwable (since RuntimeException and Error are always
implicitly
thrown)?
>
>>
>> AnnotationType:
>> accessibleMembers might not need to be volatile.
>
> AnnotationType.accessibleMembers() may be called concurrently from
> multiple threads (it is used by annotation's equals method when passed
> a foreign annotation and by AnnotationSupport.getValueArray() helper
> method used to access repeatable annotations in a foreign container
> annotation). What this method returns is a HashMap object which is not
> tolerable to unsafe publication (unlike String, for example). Volatile
> field is needed for safe publication of the HashMap instance.
I stand corrected!
>
>>
>> validateAnnotationMethod:
>> the block: label and break block seems unnecessary. I'd not worry
>> about optimizing for such invalid cases and simply run through all the
>> checks and set valid = false multiple times if so be it.
>
> I changed the method to a boolean-returning isValidAnnotationMethod()
> with multiple exit paths and moved throwing of AnnotationFormatError
> into AnnotationType constructor.
>
> Here's all that applied together with comment from Chris:
>
> http://cr.openjdk.java.net/~plevart/jdk9-dev/8029019_Annotations.optimization/webrev.02/
>
>
> plus more:
>
> AnnotationInvocationHandler.equalsImpl()
>
> - should check the number of values in both annotations and not the
> lengths of hash tables (hash table lengths are rounded to power of 2)
> - exception handling when a foreign annotation throws an unchecked
> exception from it's member method - propagate it instead of returning
> "false" from equals
>
> AnnotationInvocationHandler.asOneOfUs()
>
> - enclose the Proxy.getInvocationHandler() in doPrivileged() if needed
> otherwise we can get SecurityException
>
> AnnotationInvocationHandler.readObject()
>
> - don't modify the Map instance read from stream as it might be shared
> or unmodifiable - clone it and modify it if necessary.
Good - I had jotted down a note to ask about this in an early review draft.
>
> AnnotationParser:
>
> - skip annotations for types that are not annotation types any more
> and propagate AnnotationFormatError for invalid annotation types.
>
> AnnotationSupport.getValueArray()
>
> - exception handling cleanup.
>
> I just noticed this last thing will need another webrev and some
> discussion as handling of "oneOfUs" vs. "foreign" annotation is
> different currently. Should a RuntimeException (other than
> IncompleteAnnotationException) be propagated or wrapped with
> AnnotationFormatError? RuntimeException(s) are exceptions thrown by
> various ExceptionProxy(s) when retrieving annotation values that are
> somehow invalid.
>
> Here's the diff between webrev.01 and 02 (without changes to the test):
>
> http://cr.openjdk.java.net/~plevart/jdk9-dev/8029019_Annotations.optimization/webrev.01to02/
>
I think this all seems reasonable, but subtle behavior changes like this
needs more scrutiny than I can provide. I've discussed this offline
with Joe and sadly concluded it's probably too much, too late for 9 at
this point.
Hope you don't mind re-targetting this to JDK 10.
Thanks!
/Claes
More information about the core-libs-dev
mailing list