diff --git a/src/java.base/share/classes/java/lang/reflect/Constructor.java b/src/java.base/share/classes/java/lang/reflect/Constructor.java index 668fc575688..308890c61ad 100644 --- a/src/java.base/share/classes/java/lang/reflect/Constructor.java +++ b/src/java.base/share/classes/java/lang/reflect/Constructor.java @@ -72,8 +72,6 @@ public final class Constructor extends Executable { private final int modifiers; // Generics and annotations support private final transient String signature; - // generic info repository; lazily initialized - private transient volatile ConstructorRepository genericInfo; private final byte[] annotations; private final byte[] parameterAnnotations; @@ -88,26 +86,27 @@ public final class Constructor extends Executable { @Override ConstructorRepository getGenericInfo() { var genericInfo = this.genericInfo; - // lazily initialize repository if necessary if (genericInfo == null) { - // create and cache generic info repository - genericInfo = - ConstructorRepository.make(getSignature(), - getFactory()); + var root = this.root; + if (root != null) { + genericInfo = root.getGenericInfo(); + } else { + genericInfo = ConstructorRepository.make(getSignature(), getFactory()); + } this.genericInfo = genericInfo; } - return genericInfo; //return cached repository + return genericInfo; } - @Stable - private ConstructorAccessor constructorAccessor; - // For sharing of ConstructorAccessors. This branching structure - // is currently only two levels deep (i.e., one root Constructor - // and potentially many Constructor objects pointing to it.) - // - // If this branching structure would ever contain cycles, deadlocks can - // occur in annotation code. - private Constructor root; + /** + * Constructors are mutable due to {@link AccessibleObject#setAccessible(boolean)}. + * Thus, we return a new copy of a root each time a constructor is returned. + * Some lazily initialized immutable states can be stored on root and shared to the copies. + */ + private Constructor root; + private transient volatile ConstructorRepository genericInfo; + private @Stable ConstructorAccessor constructorAccessor; + // End shared states @Override Constructor getRoot() { @@ -143,13 +142,6 @@ public final class Constructor extends Executable { * "root" field points to this Constructor. */ Constructor copy() { - // This routine enables sharing of ConstructorAccessor objects - // among Constructor objects which refer to the same underlying - // method in the VM. (All of this contortion is only necessary - // because of the "accessibility" bit in AccessibleObject, - // which implicitly requires that new java.lang.reflect - // objects be fabricated for each reflective call on Class - // objects.) if (this.root != null) throw new IllegalArgumentException("Can not copy a non-root Constructor"); @@ -162,6 +154,7 @@ public final class Constructor extends Executable { res.root = this; // Might as well eagerly propagate this if already present res.constructorAccessor = constructorAccessor; + res.genericInfo = genericInfo; return res; } diff --git a/src/java.base/share/classes/java/lang/reflect/Field.java b/src/java.base/share/classes/java/lang/reflect/Field.java index 3e9d02d5509..5d0fe76a99a 100644 --- a/src/java.base/share/classes/java/lang/reflect/Field.java +++ b/src/java.base/share/classes/java/lang/reflect/Field.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -76,22 +76,18 @@ class Field extends AccessibleObject implements Member { private final boolean trustedFinal; // Generics and annotations support private final transient String signature; - // generic info repository; lazily initialized - private transient volatile FieldRepository genericInfo; private final byte[] annotations; - // Cached field accessor created without override - @Stable - private FieldAccessor fieldAccessor; - // Cached field accessor created with override - @Stable - private FieldAccessor overrideFieldAccessor; - // For sharing of FieldAccessors. This branching structure is - // currently only two levels deep (i.e., one root Field and - // potentially many Field objects pointing to it.) - // - // If this branching structure would ever contain cycles, deadlocks can - // occur in annotation code. - private Field root; + + /** + * Fields are mutable due to {@link AccessibleObject#setAccessible(boolean)}. + * Thus, we return a new copy of a root each time a field is returned. + * Some lazily initialized immutable states can be stored on root and shared to the copies. + */ + private Field root; + private transient volatile FieldRepository genericInfo; + private @Stable FieldAccessor fieldAccessor; // access control enabled + private @Stable FieldAccessor overrideFieldAccessor; // access control suppressed + // End shared states // Generics infrastructure @@ -107,17 +103,18 @@ class Field extends AccessibleObject implements Member { // Accessor for generic info repository private FieldRepository getGenericInfo() { var genericInfo = this.genericInfo; - // lazily initialize repository if necessary if (genericInfo == null) { - // create and cache generic info repository - genericInfo = FieldRepository.make(getGenericSignature(), - getFactory()); + var root = this.root; + if (root != null) { + genericInfo = root.getGenericInfo(); + } else { + genericInfo = FieldRepository.make(getGenericSignature(), getFactory()); + } this.genericInfo = genericInfo; } - return genericInfo; //return cached repository + return genericInfo; } - /** * Package-private constructor */ @@ -162,6 +159,7 @@ class Field extends AccessibleObject implements Member { // Might as well eagerly propagate this if already present res.fieldAccessor = fieldAccessor; res.overrideFieldAccessor = overrideFieldAccessor; + res.genericInfo = genericInfo; return res; } diff --git a/src/java.base/share/classes/java/lang/reflect/Method.java b/src/java.base/share/classes/java/lang/reflect/Method.java index 730b4b09757..6a90f91bdde 100644 --- a/src/java.base/share/classes/java/lang/reflect/Method.java +++ b/src/java.base/share/classes/java/lang/reflect/Method.java @@ -81,22 +81,20 @@ public final class Method extends Executable { private final int modifiers; // Generics and annotations support private final transient String signature; - // generic info repository; lazily initialized - private transient volatile MethodRepository genericInfo; private final byte[] annotations; private final byte[] parameterAnnotations; private final byte[] annotationDefault; - @Stable - private MethodAccessor methodAccessor; - // For sharing of MethodAccessors. This branching structure is - // currently only two levels deep (i.e., one root Method and - // potentially many Method objects pointing to it.) - // - // If this branching structure would ever contain cycles, deadlocks can - // occur in annotation code. - private Method root; - // Hash code of this object - private int hash; + + /** + * Methods are mutable due to {@link AccessibleObject#setAccessible(boolean)}. + * Thus, we return a new copy of a root each time a method is returned. + * Some lazily initialized immutable states can be stored on root and shared to the copies. + */ + private Method root; + private transient volatile MethodRepository genericInfo; + private @Stable MethodAccessor methodAccessor; + // End shared states + private int hash; // not shared right now, eligible if expensive // Generics infrastructure private String getGenericSignature() {return signature;} @@ -111,14 +109,16 @@ public final class Method extends Executable { @Override MethodRepository getGenericInfo() { var genericInfo = this.genericInfo; - // lazily initialize repository if necessary if (genericInfo == null) { - // create and cache generic info repository - genericInfo = MethodRepository.make(getGenericSignature(), - getFactory()); + var root = this.root; + if (root != null) { + genericInfo = root.getGenericInfo(); + } else { + genericInfo = MethodRepository.make(getGenericSignature(), getFactory()); + } this.genericInfo = genericInfo; } - return genericInfo; //return cached repository + return genericInfo; } /** @@ -154,13 +154,6 @@ public final class Method extends Executable { * "root" field points to this Method. */ Method copy() { - // This routine enables sharing of MethodAccessor objects - // among Method objects which refer to the same underlying - // method in the VM. (All of this contortion is only necessary - // because of the "accessibility" bit in AccessibleObject, - // which implicitly requires that new java.lang.reflect - // objects be fabricated for each reflective call on Class - // objects.) if (this.root != null) throw new IllegalArgumentException("Can not copy a non-root Method"); @@ -168,8 +161,9 @@ public final class Method extends Executable { exceptionTypes, modifiers, slot, signature, annotations, parameterAnnotations, annotationDefault); res.root = this; - // Might as well eagerly propagate this if already present + // Propagate shared states res.methodAccessor = methodAccessor; + res.genericInfo = genericInfo; return res; }