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

Reply via email to