8169425: Values computed by a ClassValue should not strongly reference the ClassValue
Peter Levart
peter.levart at gmail.com
Wed Nov 9 12:31:12 UTC 2016
Hi Paul,
On 11/09/2016 09:13 AM, Peter Levart wrote:
>
> It is not always the case that when ClassValue instance is strongly
> reachable from the associated computed value, unloading of classes and
> class loaders is prevented.
Take the following example (assume that the code is loaded by a special
- say container - app class loader):
public class MyApp {
public static final ClassValue<Object> CV = new ClassValue<Object>() {
@Override
protected Object computeValue(Class<?> type) {
return CV;
}
};
public static void main(String[] args) {
CV.get(MyApp.class); // this is OK, it only makes CV reachable
from MyApp.class
CV.get(Object.class); // WARNING!!! this will make CV reachable
from Object.class, pinning it for the VM's lifetime
}
}
So the advise could be: "Avoid referencing ClassValue instance from
associated values, but if you really must, you can do that safely only
if you limit associating values with classes that have been loaded by
the same class loader as the ClassValue instance runtime class (a
subclass of ClassValue) or any descendant class loader.
We see that what causes the problem in above situation is the fact that
the CV instance's runtime class is a subclass of ClassValue and that it
is loaded by a non-system class loader. The above situation could be
prevented by a special concrete ClassValue implementation, provided by
the platform (loaded by bootstrap CL):
public class WeakSupplierClassValue<T> extends ClassValue<T> {
private final WeakReference<Supplier<T>> supplierRef;
public WeakSupplierClassValue(Supplier<T> supplier) {
supplierRef = new WeakReference<>(supplier);
}
@Override
protected T computeValue(Class<?> type) {
Supplier<T> supplier = supplierRef.get();
if (supplier == null) {
throw new IllegalStateException("Supplier has already been
GCed");
}
return supplier.get();
}
}
...with such utility class, one could rewrite above example to:
public class MyApp {
// make CV_SUPPLIER stay at least until MyApp class is alive
private static final Supplier<Object> CV_SUPPLIER = () -> MyApp.CV;
public static final ClassValue<Object> CV =
new WeakSupplierClassValue<>(CV_SUPPLIER);
public static void main(String[] args) {
// this is OK
CV.get(MyApp.class);
// even this is OK, it makes CV reachable from Object.class,
// but CV_SUPPLIER is only weakly reachable
CV.get(Object.class);
}
}
Regards, Peter
More information about the core-libs-dev
mailing list