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