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

_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to