Never mind… I see you already did yesterday. I’m a bit behind. :) Thanks! Dan
> On Jan 15, 2019, at 6:30 PM, Daniel Kulp <dk...@apache.org> wrote: > > Freeman, > > This looks much better. Can you get this merged in? Thanks! > > Dan > > >> On Dec 24, 2018, at 4:50 AM, Freeman Fang <freeman.f...@gmail.com >> <mailto:freeman.f...@gmail.com>> wrote: >> >> Hi Dan, >> >> I just revised to use ClassValue as ProxyClassLoaderCache backend, and I >> pushed the changes to ProxyClassLoaderCache branch, would you please take a >> look? >> >> Merry Christmas! >> ------------- >> Freeman(Yue) Fang >> >> Red Hat, Inc. >> >> >> >> >> >>> On Dec 21, 2018, at 11:43 PM, Daniel Kulp <dk...@apache.org >>> <mailto:dk...@apache.org>> wrote: >>> >>> This commit has a bunch of problems. >>> >>> At no point is anything removed from these caches until the cache fills. >>> Thus, would definitely be considered a memory leak of some sort. >>> >>> Also, because the class loader is held onto strongly, the classes are >>> locked and not able to be garbage collected. This would prevent apps from >>> being unloaded, result in locked jar/wars/ears, etc… >>> >>> This really needs to be reverted before 3.3. >>> >>> My suggestion, I think, would be if you can determine which interface in >>> the array is the user one (and not CXF/JDK), use a ClassValue<ClassLoader> >>> to compute and hold this. >>> >>> >>> >>> Dan >>> >>> >>> >>>> On Dec 21, 2018, at 12:32 AM, ff...@apache.org <mailto:ff...@apache.org> >>>> wrote: >>>> >>>> This is an automated email from the ASF dual-hosted git repository. >>>> >>>> ffang pushed a commit to branch master >>>> in repository https://gitbox.apache.org/repos/asf/cxf.git >>>> <https://gitbox.apache.org/repos/asf/cxf.git> >>>> >>>> >>>> The following commit(s) were added to refs/heads/master by this push: >>>> new af33b2a [CXF-7934]Add cache for ProxyClassLoader >>>> af33b2a is described below >>>> >>>> commit af33b2a28c39504388ea370f6849a06d551b4dd1 >>>> Author: Freeman Fang <freeman.f...@gmail.com >>>> <mailto:freeman.f...@gmail.com>> >>>> AuthorDate: Fri Dec 21 13:32:19 2018 +0800 >>>> >>>> [CXF-7934]Add cache for ProxyClassLoader >>>> --- >>>> .../org/apache/cxf/common/util/ProxyHelper.java | 53 >>>> +++++++++++++++++++++- >>>> .../org/apache/cxf/jaxrs/utils/InjectionUtils.java | 50 >>>> +++++++++++++++++--- >>>> 2 files changed, 95 insertions(+), 8 deletions(-) >>>> >>>> diff --git >>>> a/core/src/main/java/org/apache/cxf/common/util/ProxyHelper.java >>>> b/core/src/main/java/org/apache/cxf/common/util/ProxyHelper.java >>>> index 7b185aa..ad83ad9 100644 >>>> --- a/core/src/main/java/org/apache/cxf/common/util/ProxyHelper.java >>>> +++ b/core/src/main/java/org/apache/cxf/common/util/ProxyHelper.java >>>> @@ -24,6 +24,13 @@ import java.lang.reflect.Method; >>>> import java.lang.reflect.Proxy; >>>> import java.security.AccessController; >>>> import java.security.PrivilegedAction; >>>> +import java.util.Collections; >>>> +import java.util.HashMap; >>>> +import java.util.Map; >>>> +import java.util.logging.Level; >>>> +import java.util.logging.Logger; >>>> + >>>> +import org.apache.cxf.common.logging.LogUtils; >>>> >>>> /** >>>> * >>>> @@ -39,8 +46,15 @@ public class ProxyHelper { >>>> } >>>> HELPER = theHelper; >>>> } >>>> - >>>> - >>>> + >>>> + private static final Logger LOG = >>>> LogUtils.getL7dLogger(ProxyHelper.class); >>>> + >>>> + protected Map<String, ClassLoader> proxyClassLoaderCache = >>>> + Collections.synchronizedMap(new HashMap<String, ClassLoader>()); >>>> + protected int cacheSize = >>>> + >>>> Integer.parseInt(System.getProperty("org.apache.cxf.proxy.classloader.size", >>>> "3000")); >>>> + >>>> + >>>> protected ProxyHelper() { >>>> } >>>> >>>> @@ -59,9 +73,26 @@ public class ProxyHelper { >>>> */ >>>> private ClassLoader getClassLoaderForInterfaces(final ClassLoader >>>> loader, final Class<?>[] interfaces) { >>>> if (canSeeAllInterfaces(loader, interfaces)) { >>>> + LOG.log(Level.FINE, "current classloader " + loader + " can >>>> see all interface"); >>>> return loader; >>>> } >>>> + String sortedNameFromInterfaceArray = >>>> getSortedNameFromInterfaceArray(interfaces); >>>> + ClassLoader cachedLoader = >>>> proxyClassLoaderCache.get(sortedNameFromInterfaceArray); >>>> + if (cachedLoader != null) { >>>> + if (canSeeAllInterfaces(cachedLoader, interfaces)) { >>>> + //found cached loader >>>> + LOG.log(Level.FINE, "find required loader from >>>> ProxyClassLoader cache with key" >>>> + + sortedNameFromInterfaceArray); >>>> + return cachedLoader; >>>> + } else { >>>> + //found cached loader somehow can't see all interfaces >>>> + LOG.log(Level.FINE, "find a loader from ProxyClassLoader >>>> cache with key " >>>> + + sortedNameFromInterfaceArray >>>> + + " but can't see all interfaces"); >>>> + } >>>> + } >>>> ProxyClassLoader combined; >>>> + LOG.log(Level.FINE, "can't find required ProxyClassLoader from >>>> cache, create a new one with parent " + loader); >>>> final SecurityManager sm = System.getSecurityManager(); >>>> if (sm == null) { >>>> combined = new ProxyClassLoader(loader, interfaces); >>>> @@ -75,9 +106,26 @@ public class ProxyHelper { >>>> } >>>> for (Class<?> currentInterface : interfaces) { >>>> combined.addLoader(getClassLoader(currentInterface)); >>>> + LOG.log(Level.FINE, "interface for new created >>>> ProxyClassLoader is " >>>> + + currentInterface.getName()); >>>> + LOG.log(Level.FINE, "interface's classloader for new created >>>> ProxyClassLoader is " >>>> + + currentInterface.getClassLoader()); >>>> } >>>> + if (proxyClassLoaderCache.size() >= cacheSize) { >>>> + LOG.log(Level.FINE, "proxyClassLoaderCache is full, need >>>> clear it"); >>>> + proxyClassLoaderCache.clear(); >>>> + } >>>> + proxyClassLoaderCache.put(sortedNameFromInterfaceArray, combined); >>>> return combined; >>>> } >>>> + >>>> + private String getSortedNameFromInterfaceArray(Class<?>[] interfaces) >>>> { >>>> + SortedArraySet<String> arraySet = new SortedArraySet<String>(); >>>> + for (Class<?> currentInterface : interfaces) { >>>> + arraySet.add(currentInterface.getName() + >>>> currentInterface.getClassLoader()); >>>> + } >>>> + return arraySet.toString(); >>>> + } >>>> >>>> private static ClassLoader getClassLoader(final Class<?> clazz) { >>>> final SecurityManager sm = System.getSecurityManager(); >>>> @@ -123,4 +171,5 @@ public class ProxyHelper { >>>> public static Object getProxy(ClassLoader loader, Class<?>[] >>>> interfaces, InvocationHandler handler) { >>>> return HELPER.getProxyInternal(loader, interfaces, handler); >>>> } >>>> + >>>> } >>>> diff --git >>>> a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java >>>> >>>> b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java >>>> index ce4d239..e2c80e1 100644 >>>> --- >>>> a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java >>>> +++ >>>> b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java >>>> @@ -137,7 +137,13 @@ public final class InjectionUtils { >>>> private static final String ENUM_CONVERSION_CASE_SENSITIVE = >>>> "enum.conversion.case.sensitive"; >>>> >>>> private static final String IGNORE_MATRIX_PARAMETERS = >>>> "ignore.matrix.parameters"; >>>> - >>>> + >>>> + private static Map<String, ProxyClassLoader> proxyClassLoaderCache = >>>> + Collections.synchronizedMap(new HashMap<String, >>>> ProxyClassLoader>()); >>>> + >>>> + private static int cacheSize = >>>> + >>>> Integer.parseInt(System.getProperty("org.apache.cxf.proxy.classloader.size", >>>> "3000")); >>>> + >>>> private InjectionUtils() { >>>> >>>> } >>>> @@ -1076,9 +1082,25 @@ public final class InjectionUtils { >>>> proxy = createThreadLocalServletApiContext(type.getName()); >>>> } >>>> if (proxy == null) { >>>> - ProxyClassLoader loader = new >>>> ProxyClassLoader(Proxy.class.getClassLoader()); >>>> - loader.addLoader(type.getClassLoader()); >>>> - loader.addLoader(ThreadLocalProxy.class.getClassLoader()); >>>> + ProxyClassLoader loader >>>> + = proxyClassLoaderCache.get(type.getName() + >>>> type.getClassLoader()); >>>> + if (loader == null >>>> + || !canSeeAllClasses(loader, new Class<?>[]{Proxy.class, >>>> type, ThreadLocalProxy.class})) { >>>> + // to avoid creating too much ProxyClassLoader to save >>>> Metaspace usage >>>> + LOG.log(Level.FINE, "can't find required ProxyClassLoader >>>> for type " + type.getName()); >>>> + LOG.log(Level.FINE, "create a new one with parent " + >>>> Proxy.class.getClassLoader()); >>>> + loader = new >>>> ProxyClassLoader(Proxy.class.getClassLoader()); >>>> + loader.addLoader(type.getClassLoader()); >>>> + LOG.log(Level.FINE, "type classloader is " + >>>> type.getClassLoader()); >>>> + loader.addLoader(ThreadLocalProxy.class.getClassLoader()); >>>> + LOG.log(Level.FINE, "ThreadLocalProxy classloader is " >>>> + + >>>> ThreadLocalProxy.class.getClassLoader().getClass().getName()); >>>> + if (proxyClassLoaderCache.size() >= cacheSize) { >>>> + LOG.log(Level.FINE, "proxyClassLoaderCache is full, >>>> need clear it"); >>>> + proxyClassLoaderCache.clear(); >>>> + } >>>> + proxyClassLoaderCache.put(type.getName() + >>>> type.getClassLoader(), loader); >>>> + } >>>> return (ThreadLocalProxy<T>)Proxy.newProxyInstance(loader, >>>> new Class[] {type, >>>> ThreadLocalProxy.class }, >>>> new ThreadLocalInvocationHandler<T>()); >>>> @@ -1086,8 +1108,24 @@ public final class InjectionUtils { >>>> >>>> return (ThreadLocalProxy<T>)proxy; >>>> } >>>> - >>>> - private static boolean isServletApiContext(String name) { >>>> + >>>> + private static boolean canSeeAllClasses(ClassLoader loader, >>>> Class<?>[] interfaces) { >>>> + for (Class<?> currentInterface : interfaces) { >>>> + String ifName = currentInterface.getName(); >>>> + try { >>>> + Class<?> ifClass = Class.forName(ifName, true, loader); >>>> + if (ifClass != currentInterface) { >>>> + return false; >>>> + } >>>> + >>>> + } catch (NoClassDefFoundError | ClassNotFoundException e) { >>>> + return false; >>>> + } >>>> + } >>>> + return true; >>>> + } >>>> + >>>> + private static boolean isServletApiContext(String name) { >>>> return name.startsWith("javax.servlet."); >>>> } >>>> >>>> >>> >>> -- >>> Daniel Kulp >>> dk...@apache.org <mailto:dk...@apache.org> <mailto:dk...@apache.org >>> <mailto:dk...@apache.org>> - http://dankulp.com/blog >>> <http://dankulp.com/blog> <http://dankulp.com/blog >>> <http://dankulp.com/blog>> >>> Talend Community Coder - http://talend.com <http://talend.com/> >>> <http://coders.talend.com/ <http://coders.talend.com/>> > > -- > Daniel Kulp > dk...@apache.org <mailto:dk...@apache.org> - http://dankulp.com/blog > <http://dankulp.com/blog> > Talend Community Coder - http://talend.com <http://coders.talend.com/> -- Daniel Kulp dk...@apache.org <mailto:dk...@apache.org> - http://dankulp.com/blog <http://dankulp.com/blog> Talend Community Coder - http://talend.com <http://coders.talend.com/>