Questions about JDK-8136353 "ClassValue preventing class unloading"

Peter Levart peter.levart at gmail.com
Fri Jan 8 08:33:58 UTC 2016


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 at 6b027549 finalized
1
CVTest$Dummy at 65c59659 finalized
2
CVTest$Dummy at 61fa1e39 finalized
...
...


Regards, Peter


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



More information about the mlvm-dev mailing list