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

Am 06.01.2016 um 21:34 schrieb Peter Levart:
Currently the cached instance of type T is reachable from Class<?>
instance and you are using Integer.TYPE in your example. Which means
that the cached instance of T is never going to be released if
ClassValue<T> instance is reachable from cached instance of T. In your
example, it is: (Dummy instance -> Dummy.class -> MyClassValue
instance). So you get this reachability chain: Integer.TYPE ->
ClassValueMap instance -> ClassValueMap.Entry[] -> ClassValueMap.Entry
-> Dummy instance -> Dummy.class -> MyClassValue instance.
Integer.TYPE is a Class<?> instance representing int type which is
loaded by bootstrap class loader, which never goes away.

So in other words, this is supposed to work if I used for example Dummy instead of Integer.TYPE? Because this is not the case (just tested with 1.8.0_66). Which means we are not talking only about bootstrap classes, but about basically any class.

Hi Jochen,

I suspect you specified MyClassValue.Dummy.class instead of Integer.TYPE ? Was Dummy.class loaded by same class loader as the MyClassValue for which the ClassValue.get() was executed. I have tried to execute the following variation of the test (no need to pack a separate jar file - you can execute if directly from within an IDE):

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

public class CVTest {

    public static class MyClassValue extends ClassValue {
        protected Object computeValue(Class type) {
            Dummy ret = new Dummy();
            Dummy.o = this;
            return ret;

    public static class Dummy {
        static Object o;

        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")
        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());
            ClassValue<?> cv = (ClassValue<?>)
            Class<?> key = classLoader.loadClass("CVTest$Dummy");
            Object value = cv.get(key);
            assert value.getClass().getClassLoader() == classLoader;
            assert key.getClassLoader() == classLoader;
            classLoader = null;
            cv = null;
            key = null;
            value = null;

...and it prints:

CVTest$Dummy@6b027549 finalized
CVTest$Dummy@65c59659 finalized
CVTest$Dummy@61fa1e39 finalized
CVTest$Dummy@24f89c62 finalized
CVTest$Dummy@5ca80d89 finalized
CVTest$Dummy@15963f1b finalized
CVTest$Dummy@242cf046 finalized
CVTest$Dummy@44d7632d finalized
CVTest$Dummy@588ffa7b finalized
CVTest$Dummy@7cc57896 finalized

...indicating that objects do get released in this case.

Regards, Peter

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?

bye Jochen
mlvm-dev mailing list

mlvm-dev mailing list

Reply via email to