8169425: Values computed by a ClassValue should not strongly reference the ClassValue
Remi Forax
forax at univ-mlv.fr
Wed Nov 9 17:29:35 UTC 2016
----- Mail original -----
> De: "Paul Sandoz" <paul.sandoz at oracle.com>
> Cc: "Core-Libs-Dev" <core-libs-dev at openjdk.java.net>
> Envoyé: Mercredi 9 Novembre 2016 17:47:34
> Objet: Re: 8169425: Values computed by a ClassValue should not strongly reference the ClassValue
> Hi Peter,
>
> Good point about if such support was added it would break the API and also (with
> Remi) about Ephemerons.
>
> You are correct in stating the constraints in more detail regarding classes in
> the same loader. However, i think that is going into more detail than I would
> prefer for what i think is an edge case. So I want in general to warn
> developers away from strongly referencing this ClassValue in the computed value
> for any associated class.
>
> If we do get strong feedback that this is a real problem we could consider
> adding a clever little static method like you suggest, with caveats that the
> computing Function might go away.
>
> At the moment I would prefer to keep things simple and say as little as
> possible.
I agree with Paul, given the number of people that use ClassValue, i think we should keep the thing simple and let people write their own class on top of ClassValue if they need it.
>
> Paul.
Rémi
>
>
>> On 9 Nov 2016, at 05:15, Peter Levart <peter.levart at gmail.com> wrote:
>>
>> Hi Paul,
>>
>> What do you think of introducing a static factory method in ClassValue in
>> addition to your @implNotes. The method would go like this (similar to
>> ThreadLocal.withInitial()):
>>
>> public abstract class ClassValue<T> {
>>
>> /**
>> * Creates a {@code ClassValue} instance which uses given {@code factory}
>> * function for computing values associated with classes passed as arguments
>> * to {@link #get} method. The given {@code factory} function will only be
>> * <a href="../ref/package-summary.html#reachability"><em>weakly
>> reachable</em></a>
>> * from the created ClassValue instance, so one must ensure that is is not
>> Garbage
>> * Collected at least until the returned ClassValue is not used any more.
>> * <p>
>> * Attempts to use created ClassValue instance to lazily calculate another
>> * associated value after the given factory function is GCed will result in
>> * {@link IllegalStateException} being thrown from the {@link #get} method.
>> *
>> * @param factory the function to be used to produce values associated with
>> * passed-in classes and created ClassValue instance
>> * @param <T> the type of values associated with created ClassValue instance
>> * @return new instance of ClassValue, weakly referencing given factory function.
>> * @since 9
>> */
>> public static <T> ClassValue<T> withWeakFactory(
>> Function<? super Class<?>, T> factory)
>> {
>> WeakReference<Function<? super Class<?>, T>> factoryRef =
>> new WeakReference<>(factory);
>>
>> return new ClassValue<T>() {
>> @Override
>> protected T computeValue(Class<?> type) {
>> Function<? super Class<?>, T> factory = factoryRef.get();
>> if (factory == null) {
>> throw new IllegalStateException(
>> "The value factory function has already been GC(ed).");
>> }
>> return factory.apply(type);
>> }
>> };
>> }
>>
>> @implNotes could point to this method with an example...
>>
>>
>> Regards, Peter
>>
>>
>> On 11/09/2016 01:49 PM, Peter Levart wrote:
>>> Or, better yet, using value factory Function instead of Supplier:
>>>
>>>
>>> public class WeakFactoryClassValue<T> extends ClassValue<T> {
>>> private final WeakReference<Function<? super Class<?>, ? extends T>> factoryRef;
>>>
>>> public WeakFactoryClassValue(Function<? super Class<?>, ? extends T> factory) {
>>> factoryRef = new WeakReference<>(factory);
>>> }
>>>
>>> @Override
>>> protected T computeValue(Class<?> type) {
>>> Function<? super Class<?>, ? extends T> factory = factoryRef.get();
>>> if (factory == null) {
>>> throw new IllegalStateException("Value factory function has already been GCed");
>>> }
>>> return factory.apply(type);
>>> }
>>> }
>>>
>>>
>>> The example would then read:
>>>
>>> public class MyApp {
>>> // make VALUE_FACTORY stay at least until MyApp class is alive
>>> private static final Function<Class<?>, Object> VALUE_FACTORY = clazz ->
>>> MyApp.CV;
>>>
>>> public static final ClassValue<Object> CV =
>>> new WeakFactoryClassValue<>(VALUE_FACTORY);
>>>
>>> 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 VALUE_FACTORY is only weakly reachable
>>> CV.get(Object.class);
>>> }
>>> }
>>>
>>>
>>>
>>> Regards, Peter
>>>
>>>
>>>
>>> On 11/09/2016 01:31 PM, Peter Levart wrote:
>>> > 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