Question in understanding ClassValue better

Peter Levart peter.levart at gmail.com
Tue May 24 13:33:09 UTC 2016



On 05/24/2016 01:41 PM, Peter Levart wrote:
> Hm,
>
> It seems that my example will not help much. It also seems that the 
> only problem with plain:
>
>     static final ClassValue<MetaClass> META_CLASS_CV = new 
> ClassValue<MetaClass>() {
>         @Override
>          protected MetaClass computeValue(Class<?> type) {
>              return new MetaClass(type);
>          }
>     };
>
> ...is the fact that MetaClass is a class loaded by non-bootstrap class 
> loader and that in case this is a Web app class loader, it prevents 
> undeployment. Can you confirm that a MetaClass instance only 
> references the 'type' Class<?> it is derived from (it's Methods, 
> Fields, etc.) and never references objects from any child class 
> loaders of the type's class loader?
>
> If that is the case, then you could replace MetaClass with a generic 
> data structure, composed of instances of bootstrap classes (HashMap, 
> ArrayList, Object[], ...). That way, Groovy runtime class loader will 
> not be "captured" by a reference from an aClass loaded by bootstrap 
> class loader.
>
> Is MetaClass a complicated data structure?

...peeking at Groovy sources, very much so. There's a solution though. 
Various Meta* classes in Groovy runtime reference at some point the 
reflective objects (Class, Method, Constructor, Field) describing the 
'type' they are derived from.

Every reference to a Class object from such Meta* object should be 
wrapped in something like the following:


public final class ClassReference extends WeakReference<Class<?>>
     implements Supplier<Class<?>> {

     private static final ConcurrentHashMap<ClassReference, 
ClassReference> MAP
         = new ConcurrentHashMap<>();
     private static final ReferenceQueue<Class<?>> QUEUE
         = new ReferenceQueue<>();

     public static ClassReference forClass(Class<?> clazz) {
         ClassReference oldRef;
         while ((oldRef = (ClassReference) QUEUE.poll()) != null) {
             MAP.remove(oldRef);
         }
         ClassReference newRef = new ClassReference(clazz);
         oldRef = MAP.putIfAbsent(newRef, newRef);
         return oldRef == null ? newRef : oldRef;
     }

     private final String name;
     private final int hash;

     private ClassReference(Class<?> clazz) {
         super(clazz, QUEUE);
         name = clazz.getName();
         hash = clazz.hashCode();
     }

     @Override
     public Class<?> get() {
         Class<?> clazz = super.get();
         if (clazz == null) {
             throw new IllegalStateException(
                 "Class " + name + " has already been unloaded");
         }
         return clazz;
     }

     @Override
     public int hashCode() {
         return hash;
     }

     @Override
     public boolean equals(Object obj) {
         Class<?> clazz;
         return obj == this ||
                (obj instanceof ClassReference &&
                 (clazz = get()) != null &&
                 clazz == ((ClassReference) obj).get());
     }
}


Every reference to a Method should be wrapped in something like this:


public final class MethodReference implements Supplier<Method> {
     private static final ClassValue<Method[]> DECLARED_METHODS_CV =
         new ClassValue<Method[]>() {
             @Override
             protected Method[] computeValue(Class<?> type) {
                 return type.getDeclaredMethods();
             }
         };

     private final ClassReference declaringClassRef;
     private final int index;

     public MethodReference(Method method) {
         Class<?> declaringClass = method.getDeclaringClass();
         declaringClassRef = ClassReference.forClass(declaringClass);
         Method[] methods = DECLARED_METHODS_CV.get(declaringClass);
         index = Arrays.asList(methods).indexOf(method);
     }

     @Override
     public Method get() {
         return DECLARED_METHODS_CV.get(declaringClassRef.get())[index];
     }

     @Override
     public int hashCode() {
         return declaringClassRef.hashCode() * 31 + index;
     }

     @Override
     public boolean equals(Object obj) {
         return obj == this || (
             obj instanceof MethodReference &&
             ((MethodReference) obj).declaringClassRef == 
this.declaringClassRef &&
             ((MethodReference) obj).index == this.index
         );
     }
}


And similar with every reference to a Constructor or Field.


In addition, the MetaClass structure should be isolated from the class 
it is derived from with what I presented in the previous message 
(ClassValue<WeakReference<MetaClass>> + ArrayList<MetaClass> referenced 
from MetaClass static field)

Would that work?

>
> Regards, Peter



More information about the mlvm-dev mailing list