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 <[email protected]> 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, [email protected] 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
>> 
>> 
>> 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 <[email protected]>
>> 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
> [email protected] <mailto:[email protected]> - http://dankulp.com/blog 
> <http://dankulp.com/blog>
> Talend Community Coder - http://talend.com <http://coders.talend.com/>

Reply via email to