Fri, 20 Sep 2013 12:11:08 +0200
8022587: ClassCache is not optimal and leaks Source instances
Reviewed-by: lagergren, attila
src/jdk/nashorn/internal/objects/Global.java | file | annotate | diff | comparison | revisions |
1.1 --- a/src/jdk/nashorn/internal/objects/Global.java Fri Sep 20 12:56:07 2013 +0530 1.2 +++ b/src/jdk/nashorn/internal/objects/Global.java Fri Sep 20 12:11:08 2013 +0200 1.3 @@ -33,6 +33,7 @@ 1.4 import java.io.PrintWriter; 1.5 import java.lang.invoke.MethodHandle; 1.6 import java.lang.invoke.MethodHandles; 1.7 +import java.lang.ref.ReferenceQueue; 1.8 import java.lang.ref.SoftReference; 1.9 import java.lang.reflect.Field; 1.10 import java.util.Arrays; 1.11 @@ -691,17 +692,41 @@ 1.12 * Cache for compiled script classes. 1.13 */ 1.14 @SuppressWarnings("serial") 1.15 - private static class ClassCache extends LinkedHashMap<Source, SoftReference<Class<?>>> { 1.16 + private static class ClassCache extends LinkedHashMap<Source, ClassReference> { 1.17 private final int size; 1.18 + private final ReferenceQueue<Class<?>> queue; 1.19 1.20 ClassCache(int size) { 1.21 super(size, 0.75f, true); 1.22 this.size = size; 1.23 + this.queue = new ReferenceQueue<>(); 1.24 + } 1.25 + 1.26 + void cache(final Source source, final Class<?> clazz) { 1.27 + put(source, new ClassReference(clazz, queue, source)); 1.28 } 1.29 1.30 @Override 1.31 - protected boolean removeEldestEntry(final Map.Entry<Source, SoftReference<Class<?>>> eldest) { 1.32 - return size() >= size; 1.33 + protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { 1.34 + return size() > size; 1.35 + } 1.36 + 1.37 + @Override 1.38 + public ClassReference get(Object key) { 1.39 + for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { 1.40 + remove(ref.source); 1.41 + } 1.42 + return super.get(key); 1.43 + } 1.44 + 1.45 + } 1.46 + 1.47 + private static class ClassReference extends SoftReference<Class<?>> { 1.48 + private final Source source; 1.49 + 1.50 + ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { 1.51 + super(clazz, queue); 1.52 + this.source = source; 1.53 } 1.54 } 1.55 1.56 @@ -709,22 +734,14 @@ 1.57 @Override 1.58 public Class<?> findCachedClass(final Source source) { 1.59 assert classCache != null : "Class cache used without being initialized"; 1.60 - SoftReference<Class<?>> ref = classCache.get(source); 1.61 - if (ref != null) { 1.62 - final Class<?> clazz = ref.get(); 1.63 - if (clazz == null) { 1.64 - classCache.remove(source); 1.65 - } 1.66 - return clazz; 1.67 - } 1.68 - 1.69 - return null; 1.70 + ClassReference ref = classCache.get(source); 1.71 + return ref != null ? ref.get() : null; 1.72 } 1.73 1.74 @Override 1.75 public void cacheClass(final Source source, final Class<?> clazz) { 1.76 assert classCache != null : "Class cache used without being initialized"; 1.77 - classCache.put(source, new SoftReference<Class<?>>(clazz)); 1.78 + classCache.cache(source, clazz); 1.79 } 1.80 1.81 private static <T> T getLazilyCreatedValue(final Object key, final Callable<T> creator, final Map<Object, T> map) {