Hi Jochen,

On 01/07/2016 03:05 PM, Jochen Theodorou wrote:
Hi Peter,

Am 06.01.2016 um 21:34 schrieb Peter Levart:
[...]

ClassValue API could use a different strategy and reference the cached
instance of type T from ClassValue<T> instance instead, but then you
would have a problem with ClassValue instances reachable from other
class loaders than the cached instances of type T.

yes, that is what I tried as well... I used my own map directly in ClassValue and avoided the map ClassValue uses natively.

So the solution for this case is basically using a Soft- or Weak-Reference in Dummy, right?

The solution to use any Class<?> key and not be dependent on it's origin (i.e. the class loader) and therefore on it's lifetime, is to decouple the cached value from the Class<?> key by an indirection through a WeakReference, but then you should make sure the cached value doesn't go away while it is still possible to retrieve it - the trick is to hook it on a list that is reachable from the ClassValue instance that constructed it:

import java.io.File;
import java.io.UncheckedIOException;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CVTest {

public static class MyClassValue extends ClassValue<WeakReference<Dummy>> {
        private final List<Dummy> dummies = new ArrayList<>();
        @Override
        protected WeakReference<Dummy> computeValue(Class type) {
            Dummy ret = new Dummy();
// make it reachable from MyClassValue instance that constructed it
            synchronized(dummies) {
                dummies.add(ret);
            }

            Dummy.o = this;

// decouple it from 'type' - make it only weakly reachable from 'type'
            return new WeakReference<>(ret);
        }
    }

    public static class Dummy {
        static Object o;

        @Override
        protected void finalize() throws Throwable {
            System.out.println(this + " finalized");
        }
    }

    public static void main(String[] args) throws Exception {
        String[] cp = System.getProperty("java.class.path")
.split(Pattern.quote(File.pathSeparator));
        URL[] urls = Stream.of(cp).map(f -> {
            try {
                return Paths.get(f).toUri().toURL();
            } catch (MalformedURLException e) {
                throw new UncheckedIOException(e);
            }
        }).collect(Collectors.toList()).toArray(new URL[0]);

        for (long i = 0; i < 10; i++) {
            URLClassLoader classLoader = new URLClassLoader(
                urls, ClassLoader.getSystemClassLoader().getParent());
            @SuppressWarnings("unchecked")
ClassValue<WeakReference<?>> cv = (ClassValue<WeakReference<?>>)
classLoader.loadClass("CVTest$MyClassValue").newInstance();

            WeakReference<?> valueRef = cv.get(Integer.TYPE);

            // while 'cv' is alive, 'value' is alive too since 'cv' keeps a
            // strong reference to 'value' in its 'dummies' list...
            Object value = valueRef.get();

            assert value != null;
            assert value.getClass().getClassLoader() == classLoader;
            System.out.println(i);
            classLoader.close();
            classLoader = null;
            cv = null;
            value = null;
            System.gc();
            Thread.sleep(200L);
        }
    }
}


...running this example produces:

0
CVTest$Dummy@6b027549 finalized
1
CVTest$Dummy@65c59659 finalized
2
CVTest$Dummy@61fa1e39 finalized
...
...


Regards, Peter



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

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

Reply via email to