From 8e5eac1acff7fa987dd3a2b4dfaaa5137b673f1d Mon Sep 17 00:00:00 2001 From: xiaoheng1 <2018154970@qq.com> Date: Tue, 13 Apr 2021 14:31:56 +0800 Subject: [PATCH] fix cache Class avoid PermGen space OOM in dubbo3.0 (#7386) * fix #6742 cache Class avoid PermGen space OOM * optimization cache Class avoid PermGen space OOM * optimization cache Class avoid PermGen space OOM * Optimize Proxy WeakReference to SoftReference --- .../apache/dubbo/common/bytecode/Proxy.java | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java index bb2b40b754..c7fc5b2775 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java @@ -20,7 +20,7 @@ import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.ReflectUtils; import java.lang.ref.Reference; -import java.lang.ref.WeakReference; +import java.lang.ref.SoftReference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -50,6 +50,8 @@ public abstract class Proxy { private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0); private static final String PACKAGE_NAME = Proxy.class.getPackage().getName(); private static final Map> PROXY_CACHE_MAP = new WeakHashMap>(); + // cache class, avoid PermGen OOM. + private static final Map> PROXY_CLASS_MAP = new WeakHashMap>(); private static final Object PENDING_GENERATION_MARKER = new Object(); @@ -103,8 +105,11 @@ public abstract class Proxy { // get cache by class loader. final Map cache; + // cache class + final Map classCache; synchronized (PROXY_CACHE_MAP) { cache = PROXY_CACHE_MAP.computeIfAbsent(cl, k -> new HashMap<>()); + classCache = PROXY_CLASS_MAP.computeIfAbsent(cl, k -> new HashMap<>()); } Proxy proxy = null; @@ -118,14 +123,38 @@ public abstract class Proxy { } } - if (value == PENDING_GENERATION_MARKER) { - try { - cache.wait(); - } catch (InterruptedException e) { + // get Class by key. + Object clazzObj = classCache.get(key); + if (null == clazzObj || clazzObj instanceof Reference) { + Class clazz = null; + if (clazzObj instanceof Reference) { + clazz = (Class) ((Reference) clazzObj).get(); + } + + if (null == clazz) { + if (value == PENDING_GENERATION_MARKER) { + try { + cache.wait(); + } catch (InterruptedException e) { + } + } else { + cache.put(key, PENDING_GENERATION_MARKER); + break; + } + } else { + try { + proxy = (Proxy) clazz.newInstance(); + return proxy; + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } finally { + if (null == proxy) { + cache.remove(key); + } else { + cache.put(key, new SoftReference<>(proxy)); + } + } } - } else { - cache.put(key, PENDING_GENERATION_MARKER); - break; } } while (true); @@ -190,7 +219,7 @@ public abstract class Proxy { ccp.setClassName(pcn); ccp.addField("public static java.lang.reflect.Method[] methods;"); ccp.addField("private " + InvocationHandler.class.getName() + " handler;"); - ccp.addConstructor(Modifier.PUBLIC, new Class[]{InvocationHandler.class}, new Class[0], "handler=$1;"); + ccp.addConstructor(Modifier.PUBLIC, new Class[] {InvocationHandler.class}, new Class[0], "handler=$1;"); ccp.addDefaultConstructor(); Class clazz = ccp.toClass(); clazz.getField("methods").set(null, methods.toArray(new Method[0])); @@ -204,6 +233,10 @@ public abstract class Proxy { ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }"); Class pc = ccm.toClass(); proxy = (Proxy) pc.newInstance(); + + synchronized (classCache) { + classCache.put(key, new SoftReference>(pc)); + } } catch (RuntimeException e) { throw e; } catch (Exception e) { @@ -220,7 +253,7 @@ public abstract class Proxy { if (proxy == null) { cache.remove(key); } else { - cache.put(key, new WeakReference(proxy)); + cache.put(key, new SoftReference(proxy)); } cache.notifyAll(); }