From a50440fadcd1aa9d8bfddc153dbde6fd55ceb9fa Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Fri, 20 Sep 2024 09:21:12 +0000 Subject: [PATCH] 8340456: Reduce overhead of proxying Object methods in ProxyGenerator Reviewed-by: liach --- .../java/lang/reflect/ProxyGenerator.java | 52 ++++++++----- .../reflect/{Proxy => proxy}/ProxyBench.java | 0 .../ProxyGeneratorBench.java} | 78 ++++++------------- 3 files changed, 55 insertions(+), 75 deletions(-) rename test/micro/org/openjdk/bench/java/lang/reflect/{Proxy => proxy}/ProxyBench.java (100%) rename test/micro/org/openjdk/bench/java/lang/reflect/{Proxy/ProxyPerf.java => proxy/ProxyGeneratorBench.java} (57%) diff --git a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java index 2e56d03c6ad..abdcaf5ae1f 100644 --- a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java +++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java @@ -114,11 +114,18 @@ final class ProxyGenerator { private static final Method OBJECT_EQUALS_METHOD; private static final Method OBJECT_TO_STRING_METHOD; + private static final String OBJECT_HASH_CODE_SIG; + private static final String OBJECT_EQUALS_SIG; + private static final String OBJECT_TO_STRING_SIG; + static { try { OBJECT_HASH_CODE_METHOD = Object.class.getMethod("hashCode"); + OBJECT_HASH_CODE_SIG = OBJECT_HASH_CODE_METHOD.toShortSignature(); OBJECT_EQUALS_METHOD = Object.class.getMethod("equals", Object.class); + OBJECT_EQUALS_SIG = OBJECT_EQUALS_METHOD.toShortSignature(); OBJECT_TO_STRING_METHOD = Object.class.getMethod("toString"); + OBJECT_TO_STRING_SIG = OBJECT_TO_STRING_METHOD.toShortSignature(); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } @@ -446,9 +453,9 @@ final class ProxyGenerator { * java.lang.Object take precedence over duplicate methods in the * proxy interfaces. */ - addProxyMethod(new ProxyMethod(OBJECT_HASH_CODE_METHOD, "m0")); - addProxyMethod(new ProxyMethod(OBJECT_EQUALS_METHOD, "m1")); - addProxyMethod(new ProxyMethod(OBJECT_TO_STRING_METHOD, "m2")); + addProxyMethod(new ProxyMethod(OBJECT_HASH_CODE_METHOD, OBJECT_HASH_CODE_SIG, "m0")); + addProxyMethod(new ProxyMethod(OBJECT_EQUALS_METHOD, OBJECT_EQUALS_SIG, "m1")); + addProxyMethod(new ProxyMethod(OBJECT_TO_STRING_METHOD, OBJECT_TO_STRING_SIG, "m2")); /* * Accumulate all of the methods from the proxy interfaces. @@ -526,7 +533,7 @@ final class ProxyGenerator { return; } } - sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType, + sigmethods.add(new ProxyMethod(m, sig, returnType, exceptionTypes, fromClass, "m" + proxyMethodCount++)); } @@ -617,11 +624,11 @@ final class ProxyGenerator { Label failLabel = cob.newLabel(); ClassEntry mhl = cp.classEntry(CD_MethodHandles_Lookup); ClassEntry iae = cp.classEntry(CD_IllegalAccessException); - cob.aload(cob.parameterSlot(0)) + cob.aload(0) .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("lookupClass", MTD_Class))) .ldc(proxyCE) .if_acmpne(failLabel) - .aload(cob.parameterSlot(0)) + .aload(0) .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("hasFullPrivilegeAccess", MTD_boolean))) .ifeq(failLabel) .invokestatic(CD_MethodHandles, "lookup", MTD_MethodHandles$Lookup) @@ -629,7 +636,7 @@ final class ProxyGenerator { .labelBinding(failLabel) .new_(iae) .dup() - .aload(cob.parameterSlot(0)) + .aload(0) .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("toString", MTD_String))) .invokespecial(cp.methodRefEntry(iae, exInit)) .athrow() @@ -650,18 +657,16 @@ final class ProxyGenerator { private final Method method; private final String shortSignature; private final Class fromClass; - private final Class[] parameterTypes; private final Class returnType; private final String methodFieldName; private Class[] exceptionTypes; private final FieldRefEntry methodField; - private ProxyMethod(Method method, String sig, Class[] parameterTypes, + private ProxyMethod(Method method, String sig, Class returnType, Class[] exceptionTypes, Class fromClass, String methodFieldName) { this.method = method; this.shortSignature = sig; - this.parameterTypes = parameterTypes; this.returnType = returnType; this.exceptionTypes = exceptionTypes; this.fromClass = fromClass; @@ -670,14 +675,17 @@ final class ProxyGenerator { cp.nameAndTypeEntry(methodFieldName, CD_Method)); } + private Class[] parameterTypes() { + return method.getSharedParameterTypes(); + } + /** * Create a new specific ProxyMethod with a specific field name * * @param method The method for which to create a proxy */ - private ProxyMethod(Method method, String methodFieldName) { - this(method, method.toShortSignature(), - method.getSharedParameterTypes(), method.getReturnType(), + private ProxyMethod(Method method, String sig, String methodFieldName) { + this(method, sig, method.getReturnType(), method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName); } @@ -685,17 +693,18 @@ final class ProxyGenerator { * Generate this method, including the code and exception table entry. */ private void generateMethod(ClassBuilder clb) { - var desc = methodTypeDesc(returnType, parameterTypes); + var desc = methodTypeDesc(returnType, parameterTypes()); int accessFlags = (method.isVarArgs()) ? ACC_VARARGS | ACC_PUBLIC | ACC_FINAL : ACC_PUBLIC | ACC_FINAL; - var catchList = computeUniqueCatchList(exceptionTypes); clb.withMethod(method.getName(), desc, accessFlags, mb -> mb.with(ExceptionsAttribute.of(toClassEntries(cp, List.of(exceptionTypes)))) .withCode(cob -> { + var catchList = computeUniqueCatchList(exceptionTypes); cob.aload(cob.receiverSlot()) .getfield(handlerField) .aload(cob.receiverSlot()) .getstatic(methodField); + Class[] parameterTypes = parameterTypes(); if (parameterTypes.length > 0) { // Create an array and fill with the parameters converting primitives to wrappers cob.loadConstant(parameterTypes.length) @@ -784,6 +793,7 @@ final class ProxyGenerator { var cp = cob.constantPool(); codeClassForName(cob, fromClass); + Class[] parameterTypes = parameterTypes(); cob.ldc(method.getName()) .loadConstant(parameterTypes.length) .anewarray(classCE); @@ -817,10 +827,14 @@ final class ProxyGenerator { * loader is anticipated at local variable index 0. */ private void codeClassForName(CodeBuilder cob, Class cl) { - cob.ldc(cl.getName()) - .iconst_0() // false - .aload(0)// classLoader - .invokestatic(classForName); + if (cl == Object.class) { + cob.ldc(objectCE); + } else { + cob.ldc(cl.getName()) + .iconst_0() // false + .aload(0)// classLoader + .invokestatic(classForName); + } } @Override diff --git a/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyBench.java b/test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyBench.java similarity index 100% rename from test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyBench.java rename to test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyBench.java diff --git a/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyPerf.java b/test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyGeneratorBench.java similarity index 57% rename from test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyPerf.java rename to test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyGeneratorBench.java index 22fe4d3ef28..33934317300 100644 --- a/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyPerf.java +++ b/test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyGeneratorBench.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,6 @@ import java.util.List; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.CompilerControl; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; @@ -36,37 +35,29 @@ import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.infra.Blackhole; - import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.concurrent.TimeUnit; /** * Benchmark measuring java.lang.reflect.ProxyGenerator.generateProxyClass. * It bypasses the cache of proxies to measure the time to construct a proxy. */ -@Warmup(iterations = 5) -@Measurement(iterations = 10) -@Fork(value = 1) +@Warmup(iterations = 5, time = 2) +@Measurement(iterations = 5, time = 2) +@Fork(value = 1, jvmArgsPrepend = "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED") @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) -public class ProxyPerf { +public class ProxyGeneratorBench { /** * Sample results from a Dell T7610. * Benchmark Mode Cnt Score Error Units * ProxyPerf.genIntf_1 avgt 10 35325.428 +/- 780.459 ns/op - * ProxyPerf.genIntf_1_V49 avgt 10 34309.423 +/- 727.188 ns/op * ProxyPerf.genStringsIntf_3 avgt 10 46600.366 +/- 663.812 ns/op - * ProxyPerf.genStringsIntf_3_V49 avgt 10 45911.817 +/- 1598.536 ns/op * ProxyPerf.genZeroParams avgt 10 33245.048 +/- 437.988 ns/op - * ProxyPerf.genZeroParams_V49 avgt 10 32954.254 +/- 1041.932 ns/op - * ProxyPerf.getPrimsIntf_2 avgt 10 43987.819 +/- 837.443 ns/op - * ProxyPerf.getPrimsIntf_2_V49 avgt 10 42863.462 +/- 1193.480 ns/op + * ProxyPerf.genPrimsIntf_2 avgt 10 43987.819 +/- 837.443 ns/op */ public interface Intf_1 { @@ -84,7 +75,6 @@ public class ProxyPerf { public String m2String(String s1, String s2); } - private InvocationHandler handler; private ClassLoader classloader; private Method proxyGen; private Method proxyGenV49; @@ -92,19 +82,11 @@ public class ProxyPerf { @Setup public void setup() { try { - handler = (Object proxy, Method method, Object[] args) -> null; classloader = ClassLoader.getSystemClassLoader(); Class proxyGenClass = Class.forName("java.lang.reflect.ProxyGenerator"); proxyGen = proxyGenClass.getDeclaredMethod("generateProxyClass", ClassLoader.class, String.class, java.util.List.class, int.class); proxyGen.setAccessible(true); - - // Init access to the old Proxy generator - Class proxyGenClassV49 = Class.forName("java.lang.reflect.ProxyGenerator_v49"); - proxyGenV49 = proxyGenClassV49.getDeclaredMethod("generateProxyClass", - String.class, java.util.List.class, int.class); - proxyGenV49.setAccessible(true); - } catch (Exception ex) { ex.printStackTrace(); throw new RuntimeException("ProxyClass setup fails", ex); @@ -112,51 +94,35 @@ public class ProxyPerf { } @Benchmark - public void genZeroParams(Blackhole bh) throws Exception { + public Object genZeroParams() throws Exception { List> interfaces = List.of(Runnable.class); - bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + return proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1); } @Benchmark - public void genIntf_1(Blackhole bh) throws Exception { + public Object genIntf_1() throws Exception { List> interfaces = List.of(Intf_1.class); - bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + return proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1); } @Benchmark - public void getPrimsIntf_2(Blackhole bh) throws Exception { + public Object genPrimsIntf_2() throws Exception { List> interfaces = List.of(Intf_2.class); - bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + return proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1); } + @Benchmark - public void genStringsIntf_3(Blackhole bh) throws Exception { + public Object genStringsIntf_3() throws Exception { List> interfaces = List.of(Intf_3.class); - bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + return proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1); } - // Generate using the V49inal generator for comparison - - @Benchmark - public void genZeroParams_V49(Blackhole bh) throws Exception { - List> interfaces = List.of(Runnable.class); - bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); + public static void main(String... args) throws Exception { + var benchmark = new ProxyGeneratorBench(); + benchmark.setup(); + benchmark.genZeroParams(); + benchmark.genIntf_1(); + benchmark.genPrimsIntf_2(); + benchmark.genStringsIntf_3(); } - - @Benchmark - public void genIntf_1_V49(Blackhole bh) throws Exception { - List> interfaces = List.of(Intf_1.class); - bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); - } - - @Benchmark - public void getPrimsIntf_2_V49(Blackhole bh) throws Exception { - List> interfaces = List.of(Intf_2.class); - bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); - } - @Benchmark - public void genStringsIntf_3_V49(Blackhole bh) throws Exception { - List> interfaces = List.of(Intf_3.class); - bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); - } - }