RFR: 8159746: (proxy) Support for default methods
Mandy Chung
mchung at openjdk.java.net
Fri Nov 20 19:15:07 UTC 2020
On Fri, 20 Nov 2020 11:18:32 GMT, Peter Levart <plevart at openjdk.org> wrote:
>> Hi Mandy,
>> I re-ran the benchmark on your latest version (the static API) and I get similar results as you:
>>
>> Benchmark Mode Cnt Score Error Units
>> ProxyBench.implClass avgt 5 3.745 ± 0.033 ns/op
>> ProxyBench.implProxy avgt 5 29.826 ± 0.183 ns/op
>> ProxyBench.ppImplClass avgt 5 3.683 ± 0.009 ns/op
>> ProxyBench.ppImplProxy avgt 5 29.124 ± 0.535 ns/op
>>
>> I also tried a variant where the access check in the invokeDefault static method is not pre-screened with checking of the interface modifiers and package export status but relies on Method.checkAccess cache alone:
>>
>> // access check
>> Class<?> caller = Reflection.getCallerClass();
>> int modifiers = method.getModifiers();
>> method.checkAccess(caller, intf, proxyClass, modifiers);
>>
>> ... and the results are not worse, even marginally better:
>>
>> Benchmark Mode Cnt Score Error Units
>> ProxyBench.implClass avgt 5 3.724 ± 0.012 ns/op
>> ProxyBench.implProxy avgt 5 29.138 ± 0.271 ns/op
>> ProxyBench.ppImplClass avgt 5 3.744 ± 0.009 ns/op
>> ProxyBench.ppImplProxy avgt 5 28.961 ± 0.182 ns/op
>>
>> I think this looks reasonably good.
>
> In my previous attempts when I was modifying the ProxyGenerator I noticed that generated proxy fields holding Method objects are just "static" but could be "static final". If they were "static final", JIT could constant-fold the Method instances. If this was combined with marking some fields in Method as @Stable, this could improve optimization of code a bit further. I did this with the following patch:
>
> Index: src/java.base/share/classes/java/lang/reflect/InvocationHandler.java
> IDEA additional info:
> Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
> <+>UTF-8
> ===================================================================
> --- src/java.base/share/classes/java/lang/reflect/InvocationHandler.java (revision 8352a20bf54a085a97d3f703b5dab482d3f9eccc)
> +++ src/java.base/share/classes/java/lang/reflect/InvocationHandler.java (date 1605869089804)
> @@ -271,15 +271,10 @@
> throw new IllegalArgumentException(""" + method + "" is not a default method");
> }
> Class<?> intf = method.getDeclaringClass();
> - // access check if it is a non-public proxy interface or not unconditionally exported
> - if (!Modifier.isPublic(intf.getModifiers()) ||
> - !intf.getModule().isExported(intf.getPackageName())) {
> - // throw IAE if the caller class has no access to the default method
> - // same access check to Method::invoke on the default method
> - int modifiers = method.getModifiers();
> - Class<?> caller = Reflection.getCallerClass();
> - method.checkAccess(caller, intf, proxyClass, modifiers);
> - }
> + // access check
> + Class<?> caller = Reflection.getCallerClass();
> + int modifiers = method.getModifiers();
> + method.checkAccess(caller, intf, proxyClass, modifiers);
>
> MethodHandle mh = Proxy.defaultMethodHandle(proxyClass, method);
> // invoke the super method
> Index: src/java.base/share/classes/java/lang/reflect/Method.java
> IDEA additional info:
> Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
> <+>UTF-8
> ===================================================================
> --- src/java.base/share/classes/java/lang/reflect/Method.java (revision 8352a20bf54a085a97d3f703b5dab482d3f9eccc)
> +++ src/java.base/share/classes/java/lang/reflect/Method.java (date 1605870445135)
> @@ -31,6 +31,7 @@
> import jdk.internal.reflect.Reflection;
> import jdk.internal.vm.annotation.ForceInline;
> import jdk.internal.vm.annotation.IntrinsicCandidate;
> +import jdk.internal.vm.annotation.Stable;
> import sun.reflect.annotation.ExceptionProxy;
> import sun.reflect.annotation.TypeNotPresentExceptionProxy;
> import sun.reflect.generics.repository.MethodRepository;
> @@ -66,7 +67,7 @@
> * @since 1.1
> */
> public final class Method extends Executable {
> - private Class<?> clazz;
> + @Stable private Class<?> clazz;
> private int slot;
> // This is guaranteed to be interned by the VM in the 1.4
> // reflection implementation
> @@ -74,7 +75,7 @@
> private Class<?> returnType;
> private Class<?>[] parameterTypes;
> private Class<?>[] exceptionTypes;
> - private int modifiers;
> + @Stable private int modifiers;
> // Generics and annotations support
> private transient String signature;
> // generic info repository; lazily initialized
> Index: src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java
> IDEA additional info:
> Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
> <+>UTF-8
> ===================================================================
> --- src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java (revision 8352a20bf54a085a97d3f703b5dab482d3f9eccc)
> +++ src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java (date 1605870027579)
> @@ -490,7 +490,7 @@
> for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
> for (ProxyMethod pm : sigmethods) {
> // add static field for the Method object
> - visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName,
> + visitField(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL, pm.methodFieldName,
> LJLR_METHOD, null, null);
>
> // Generate code for proxy method
>
> ...and this makes the following results:
>
> Benchmark Mode Cnt Score Error Units
> ProxyBench.implClass avgt 5 3.766 ± 0.040 ns/op
> ProxyBench.implProxy avgt 5 26.847 ± 0.626 ns/op
> ProxyBench.ppImplClass avgt 5 3.700 ± 0.017 ns/op
> ProxyBench.ppImplProxy avgt 5 26.322 ± 0.048 ns/op
>
> But this can be changed as a follow-up patch that also takes care of Constructor and Field.
@plevart I'm okay with this slight performance improvement.
-------------
PR: https://git.openjdk.java.net/jdk/pull/313
More information about the core-libs-dev
mailing list