Skip to content

Commit 88a6d87

Browse files
[GR-46636] Improve performance of java.lang.reflect.Proxy.isProxyClass().
PullRequest: graal/14910
2 parents 1354b8d + a5f24e4 commit 88a6d87

File tree

4 files changed

+83
-53
lines changed

4 files changed

+83
-53
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,6 @@
7272
import java.util.Optional;
7373
import java.util.StringJoiner;
7474

75-
import com.oracle.svm.core.reflect.fieldaccessor.UnsafeFieldAccessorFactory;
76-
import jdk.internal.access.JavaLangReflectAccess;
77-
import jdk.internal.reflect.FieldAccessor;
7875
import org.graalvm.compiler.core.common.NumUtil;
7976
import org.graalvm.compiler.core.common.SuppressFBWarnings;
8077
import org.graalvm.nativeimage.AnnotationAccess;
@@ -114,13 +111,16 @@
114111
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder.MethodDescriptor;
115112
import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent;
116113
import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool;
114+
import com.oracle.svm.core.reflect.fieldaccessor.UnsafeFieldAccessorFactory;
117115
import com.oracle.svm.core.util.LazyFinalReference;
118116
import com.oracle.svm.core.util.VMError;
119117
import com.oracle.svm.util.ReflectionUtil;
120118
import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError;
121119

120+
import jdk.internal.access.JavaLangReflectAccess;
122121
import jdk.internal.misc.Unsafe;
123122
import jdk.internal.reflect.CallerSensitive;
123+
import jdk.internal.reflect.FieldAccessor;
124124
import jdk.internal.reflect.Reflection;
125125
import jdk.internal.reflect.ReflectionFactory;
126126
import sun.reflect.annotation.AnnotationType;
@@ -246,13 +246,16 @@ public final class DynamicHub implements AnnotatedElement, java.lang.reflect.Typ
246246
*/
247247
private static final int IS_LAMBDA_FORM_HIDDEN_BIT = 9;
248248

249+
/** Similar to {@link #flags}, but non-final because {@link #setData} sets the value. */
249250
@UnknownPrimitiveField(availability = AfterHostedUniverse.class)//
250-
private byte instantiationFlags;
251+
private byte additionalFlags;
251252

252253
/** Has the type been discovered as instantiated by the static analysis? */
253254
private static final int IS_INSTANTIATED_BIT = 0;
254255
/** Can this class be instantiated as an instance. */
255256
private static final int CAN_INSTANTIATE_AS_INSTANCE_BIT = 1;
257+
/** Is the class a proxy class according to {@link java.lang.reflect.Proxy#isProxyClass}? */
258+
private static final int IS_PROXY_CLASS_BIT = 2;
256259

257260
/**
258261
* The {@link Modifier modifiers} of this class.
@@ -427,7 +430,7 @@ public void setClassInitializationInfo(ClassInitializationInfo classInitializati
427430

428431
@Platforms(Platform.HOSTED_ONLY.class)
429432
public void setData(int layoutEncoding, int typeID, int monitorOffset, int optionalIdentityHashOffset, short typeCheckStart, short typeCheckRange, short typeCheckSlot,
430-
short[] typeCheckSlots, CFunctionPointer[] vtable, long referenceMapIndex, boolean isInstantiated, boolean canInstantiateAsInstance) {
433+
short[] typeCheckSlots, CFunctionPointer[] vtable, long referenceMapIndex, boolean isInstantiated, boolean canInstantiateAsInstance, boolean isProxyClass) {
431434
assert this.vtable == null : "Initialization must be called only once";
432435
assert !(!isInstantiated && canInstantiateAsInstance);
433436
if (LayoutEncoding.isPureInstance(layoutEncoding)) {
@@ -451,7 +454,9 @@ public void setData(int layoutEncoding, int typeID, int monitorOffset, int optio
451454
throw VMError.shouldNotReachHere("Reference map index not within integer range, need to switch field from int to long");
452455
}
453456
this.referenceMapIndex = (int) referenceMapIndex;
454-
this.instantiationFlags = NumUtil.safeToUByte(makeFlag(IS_INSTANTIATED_BIT, isInstantiated) | makeFlag(CAN_INSTANTIATE_AS_INSTANCE_BIT, canInstantiateAsInstance));
457+
this.additionalFlags = NumUtil.safeToUByte(makeFlag(IS_INSTANTIATED_BIT, isInstantiated) |
458+
makeFlag(CAN_INSTANTIATE_AS_INSTANCE_BIT, canInstantiateAsInstance) |
459+
makeFlag(IS_PROXY_CLASS_BIT, isProxyClass));
455460
}
456461

457462
@Platforms(Platform.HOSTED_ONLY.class)
@@ -641,11 +646,15 @@ public int getReferenceMapIndex() {
641646
}
642647

643648
public boolean isInstantiated() {
644-
return isFlagSet(instantiationFlags, IS_INSTANTIATED_BIT);
649+
return isFlagSet(additionalFlags, IS_INSTANTIATED_BIT);
645650
}
646651

647652
public boolean canInstantiateAsInstance() {
648-
return isFlagSet(instantiationFlags, CAN_INSTANTIATE_AS_INSTANCE_BIT);
653+
return isFlagSet(additionalFlags, CAN_INSTANTIATE_AS_INSTANCE_BIT);
654+
}
655+
656+
public boolean isProxyClass() {
657+
return isFlagSet(additionalFlags, IS_PROXY_CLASS_BIT);
649658
}
650659

651660
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,24 @@
2626

2727
import java.lang.reflect.InvocationHandler;
2828
import java.util.Arrays;
29-
import java.util.Map;
30-
import java.util.concurrent.ConcurrentHashMap;
3129
import java.util.regex.Pattern;
3230

31+
import org.graalvm.collections.EconomicMap;
32+
import org.graalvm.collections.EconomicSet;
33+
import org.graalvm.collections.Equivalence;
3334
import org.graalvm.compiler.debug.GraalError;
3435
import org.graalvm.nativeimage.Platform;
3536
import org.graalvm.nativeimage.Platforms;
3637
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
3738
import org.graalvm.nativeimage.hosted.RuntimeReflection;
3839

40+
import com.oracle.svm.core.SubstrateUtil;
3941
import com.oracle.svm.core.configure.ConfigurationFiles;
4042
import com.oracle.svm.core.hub.DynamicHub;
4143
import com.oracle.svm.core.hub.PredefinedClassesSupport;
4244
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
4345
import com.oracle.svm.core.option.SubstrateOptionsParser;
46+
import com.oracle.svm.core.util.ImageHeapMap;
4447
import com.oracle.svm.core.util.VMError;
4548
import com.oracle.svm.util.ClassUtil;
4649
import com.oracle.svm.util.LogUtils;
@@ -82,71 +85,78 @@ public String toString() {
8285
}
8386
}
8487

85-
private final Map<ProxyCacheKey, Object> proxyCache;
88+
private final EconomicSet<Class<?>> hostedProxyClasses = EconomicSet.create(Equivalence.IDENTITY);
89+
private final EconomicMap<ProxyCacheKey, Object> proxyCache = ImageHeapMap.create();
8690

91+
@Platforms(Platform.HOSTED_ONLY.class)
8792
public DynamicProxySupport() {
88-
this.proxyCache = new ConcurrentHashMap<>();
8993
}
9094

91-
@Platforms(Platform.HOSTED_ONLY.class)
9295
@Override
93-
public void addProxyClass(Class<?>... interfaces) {
96+
@Platforms(Platform.HOSTED_ONLY.class)
97+
public synchronized void addProxyClass(Class<?>... interfaces) {
9498
/*
9599
* Make a defensive copy of the interfaces array to protect against the caller modifying the
96100
* array.
97101
*/
98-
final Class<?>[] intfs = interfaces.clone();
102+
Class<?>[] intfs = interfaces.clone();
99103
ProxyCacheKey key = new ProxyCacheKey(intfs);
100104

101-
proxyCache.computeIfAbsent(key, k -> {
102-
try {
103-
Class<?> clazz = createProxyClassFromImplementedInterfaces(intfs);
104-
105-
boolean isPredefinedProxy = Arrays.stream(interfaces).anyMatch(PredefinedClassesSupport::isPredefined);
105+
if (!proxyCache.containsKey(key)) {
106+
proxyCache.put(key, createProxyClass(intfs));
107+
}
108+
}
106109

107-
if (isPredefinedProxy) {
108-
/*
109-
* Treat the proxy as a predefined class so that we can set its class loader to
110-
* the loader passed at runtime. If one of the interfaces is a predefined class,
111-
* this can be required so that the classes can actually see each other
112-
* according to the runtime class loader hierarchy.
113-
*/
114-
PredefinedClassesSupport.registerClass(clazz);
115-
RuntimeClassInitialization.initializeAtRunTime(clazz);
116-
}
110+
@Platforms(Platform.HOSTED_ONLY.class)
111+
private Object createProxyClass(Class<?>[] interfaces) {
112+
try {
113+
Class<?> clazz = createProxyClassFromImplementedInterfaces(interfaces);
117114

115+
boolean isPredefinedProxy = Arrays.stream(interfaces).anyMatch(PredefinedClassesSupport::isPredefined);
116+
if (isPredefinedProxy) {
118117
/*
119-
* The constructor of the generated dynamic proxy class that takes a
120-
* `java.lang.reflect.InvocationHandler` argument, i.e., the one reflectively
121-
* invoked by `java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[],
122-
* InvocationHandler)`, is registered for reflection so that dynamic proxy instances
123-
* can be allocated at run time.
118+
* Treat the proxy as a predefined class so that we can set its class loader to the
119+
* loader passed at runtime. If one of the interfaces is a predefined class, this
120+
* can be required so that the classes can actually see each other according to the
121+
* runtime class loader hierarchy.
124122
*/
125-
RuntimeReflection.register(ReflectionUtil.lookupConstructor(clazz, InvocationHandler.class));
123+
PredefinedClassesSupport.registerClass(clazz);
124+
RuntimeClassInitialization.initializeAtRunTime(clazz);
125+
}
126126

127-
/*
128-
* The proxy class reflectively looks up the methods of the interfaces it implements
129-
* to pass a Method object to InvocationHandler.
130-
*/
131-
for (Class<?> intf : intfs) {
132-
RuntimeReflection.register(intf.getMethods());
133-
}
134-
135-
return clazz;
136-
} catch (Throwable t) {
137-
LogUtils.warning("Could not create a proxy class from list of interfaces: %s. Reason: %s", Arrays.toString(interfaces), t.getMessage());
138-
return t;
127+
/*
128+
* The constructor of the generated dynamic proxy class that takes a
129+
* `java.lang.reflect.InvocationHandler` argument, i.e., the one reflectively invoked by
130+
* `java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[],
131+
* InvocationHandler)`, is registered for reflection so that dynamic proxy instances can
132+
* be allocated at run time.
133+
*/
134+
RuntimeReflection.register(ReflectionUtil.lookupConstructor(clazz, InvocationHandler.class));
135+
136+
/*
137+
* The proxy class reflectively looks up the methods of the interfaces it implements to
138+
* pass a Method object to InvocationHandler.
139+
*/
140+
for (Class<?> intf : interfaces) {
141+
RuntimeReflection.register(intf.getMethods());
139142
}
140-
});
143+
144+
hostedProxyClasses.add(clazz);
145+
return clazz;
146+
} catch (Throwable t) {
147+
LogUtils.warning("Could not create a proxy class from list of interfaces: %s. Reason: %s", Arrays.toString(interfaces), t.getMessage());
148+
return t;
149+
}
141150
}
142151

143152
@Override
153+
@Platforms(Platform.HOSTED_ONLY.class)
144154
public Class<?> createProxyClassForSerialization(Class<?>... interfaces) {
145155
final Class<?>[] intfs = interfaces.clone();
146-
147156
return createProxyClassFromImplementedInterfaces(intfs);
148157
}
149158

159+
@Platforms(Platform.HOSTED_ONLY.class)
150160
private static Class<?> createProxyClassFromImplementedInterfaces(Class<?>[] interfaces) {
151161
return getJdkProxyClass(getCommonClassLoaderOrFail(null, interfaces), interfaces);
152162
}
@@ -228,7 +238,15 @@ private static void describeLoaderChain(StringBuilder b, ClassLoader loader) {
228238

229239
@Override
230240
public boolean isProxyClass(Class<?> clazz) {
231-
return proxyCache.containsValue(clazz);
241+
if (SubstrateUtil.HOSTED) {
242+
return isHostedProxyClass(clazz);
243+
}
244+
return DynamicHub.fromClass(clazz).isProxyClass();
245+
}
246+
247+
@Platforms(Platform.HOSTED_ONLY.class)
248+
private synchronized boolean isHostedProxyClass(Class<?> clazz) {
249+
return hostedProxyClasses.contains(clazz);
232250
}
233251

234252
@SuppressWarnings("deprecation")

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import com.oracle.svm.core.hub.DynamicHub;
8585
import com.oracle.svm.core.hub.DynamicHubSupport;
8686
import com.oracle.svm.core.hub.LayoutEncoding;
87+
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
8788
import com.oracle.svm.core.meta.MethodPointer;
8889
import com.oracle.svm.core.reflect.SubstrateConstructorAccessor;
8990
import com.oracle.svm.core.reflect.SubstrateMethodAccessor;
@@ -1065,9 +1066,11 @@ private void buildHubs() {
10651066
assert ((SubstrateReferenceMap) referenceMap).hasNoDerivedOffsets();
10661067
long referenceMapIndex = referenceMapEncoder.lookupEncoding(referenceMap);
10671068

1069+
boolean isProxyClass = ImageSingletons.lookup(DynamicProxyRegistry.class).isProxyClass(type.getJavaClass());
1070+
10681071
DynamicHub hub = type.getHub();
10691072
hub.setData(layoutHelper, type.getTypeID(), monitorOffset, optionalIdHashOffset, type.getTypeCheckStart(), type.getTypeCheckRange(),
1070-
type.getTypeCheckSlot(), type.getTypeCheckSlots(), vtable, referenceMapIndex, type.isInstantiated(), canInstantiateAsInstance);
1073+
type.getTypeCheckSlot(), type.getTypeCheckSlots(), vtable, referenceMapIndex, type.isInstantiated(), canInstantiateAsInstance, isProxyClass);
10711074
}
10721075
}
10731076

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@
3535
import com.oracle.svm.core.configure.ConfigurationFile;
3636
import com.oracle.svm.core.configure.ConfigurationFiles;
3737
import com.oracle.svm.core.configure.ProxyConfigurationParser;
38+
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
3839
import com.oracle.svm.core.feature.InternalFeature;
3940
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
4041
import com.oracle.svm.core.reflect.proxy.DynamicProxySupport;
41-
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
4242
import com.oracle.svm.hosted.ConfigurationTypeResolver;
4343
import com.oracle.svm.hosted.FallbackFeature;
4444
import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl;

0 commit comments

Comments
 (0)