From 904c1dcc84c8c3a08357d089730935d5b3a2ef93 Mon Sep 17 00:00:00 2001 From: Tumi Date: Mon, 23 Dec 2013 02:15:32 +0100 Subject: [PATCH] Solved PermGen memory leak in AccessClassLoader Solved the PermGen memory leak due to static strong references to the class loaders (and minor performance improvement avoiding call to Method.setAccessible() if unnecessary). --- .../reflectasm/AccessClassLoader.java | 71 +++++++++++++++---- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/src/com/esotericsoftware/reflectasm/AccessClassLoader.java b/src/com/esotericsoftware/reflectasm/AccessClassLoader.java index 9a5d62f..202f168 100644 --- a/src/com/esotericsoftware/reflectasm/AccessClassLoader.java +++ b/src/com/esotericsoftware/reflectasm/AccessClassLoader.java @@ -1,34 +1,69 @@ + package com.esotericsoftware.reflectasm; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.security.ProtectionDomain; -import java.util.ArrayList; +import java.util.WeakHashMap; class AccessClassLoader extends ClassLoader { - static private final ArrayList accessClassLoaders = new ArrayList(); - + // Weak-references to ClassLoaders, to avoid PermGen memory leaks for example + // in AppServers/WebContainters if the reflectasm framework (including this class) + // is loaded outside the deployed applications (WAR/EAR) using ReflectASM/Kryo + // (exts, user classpath, etc). + // + // The key is the parent ClassLoader and the value is the AccessClassLoader + // Both are weak-referenced in the HashTable. + static private final WeakHashMap> accessClassLoaders = new WeakHashMap>(); + // Fast-path for classes loaded in the same ClassLoader than this Class + static private final ClassLoader selfContextParentClassLoader = getParentClassLoader(AccessClassLoader.class); + static private volatile AccessClassLoader selfContextAccessClassLoader = new AccessClassLoader(selfContextParentClassLoader); + static AccessClassLoader get (Class type) { - ClassLoader parent = type.getClassLoader(); - synchronized (accessClassLoaders) { - for (int i = 0, n = accessClassLoaders.size(); i < n; i++) { - AccessClassLoader accessClassLoader = accessClassLoaders.get(i); - if (accessClassLoader.getParent() == parent) return accessClassLoader; + ClassLoader parent = getParentClassLoader(type); + // 1. fast-path: + if (selfContextParentClassLoader.equals(parent)) { + if (selfContextAccessClassLoader==null) { + // DCL with volatile semantics + synchronized (accessClassLoaders) { + if (selfContextAccessClassLoader==null) + selfContextAccessClassLoader = new AccessClassLoader(selfContextParentClassLoader); + } + } + return selfContextAccessClassLoader; + } + // 2. normal search: + synchronized (accessClassLoaders) { + WeakReference ref = accessClassLoaders.get(parent); + if (ref!=null) { + AccessClassLoader accessClassLoader = ref.get(); + if (accessClassLoader!=null) return accessClassLoader; + else accessClassLoaders.remove(parent); // the value has been GC-reclaimed, but still not the key (defensive sanity) } - if(parent == null) parent = ClassLoader.getSystemClassLoader(); AccessClassLoader accessClassLoader = new AccessClassLoader(parent); - accessClassLoaders.add(accessClassLoader); + accessClassLoaders.put(parent, new WeakReference(accessClassLoader)); return accessClassLoader; } } - static void remove (ClassLoader parent) { - synchronized (accessClassLoaders) { - for (int i = accessClassLoaders.size() - 1; i >= 0; i--) { - AccessClassLoader accessClassLoader = accessClassLoaders.get(i); - if (accessClassLoader.getParent() == parent) accessClassLoaders.remove(i); + public static void remove (ClassLoader parent) { + // 1. fast-path: + if (selfContextParentClassLoader.equals(parent)) { + selfContextAccessClassLoader = null; + } + else { + // 2. normal search: + synchronized (accessClassLoaders) { + accessClassLoaders.remove(parent); } } } + + public static int activeAccessClassLoaders() { + int sz = accessClassLoaders.size(); + if (selfContextAccessClassLoader!=null) sz++; + return sz; + } private AccessClassLoader (ClassLoader parent) { super(parent); @@ -55,4 +90,10 @@ class AccessClassLoader extends ClassLoader { } return defineClass(name, bytes, 0, bytes.length, getClass().getProtectionDomain()); } + + private static ClassLoader getParentClassLoader(Class type) { + ClassLoader parent = type.getClassLoader(); + if (parent == null) parent = ClassLoader.getSystemClassLoader(); + return parent; + } }