Repository: groovy Updated Branches: refs/heads/GROOVY_2_5_X ca296db62 -> 3341dcd9a
GROOVY-8858: Refine GCL to avoid occupying Permanent Area/Metaspace repeatedly for same source code(closes #816) Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/3341dcd9 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/3341dcd9 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/3341dcd9 Branch: refs/heads/GROOVY_2_5_X Commit: 3341dcd9a4f151b71b90ceb0be59fcdca403c300 Parents: ca296db Author: Daniel Sun <sun...@apache.org> Authored: Sun Oct 28 11:51:14 2018 +0800 Committer: Daniel Sun <sun...@apache.org> Committed: Sun Oct 28 11:51:14 2018 +0800 ---------------------------------------------------------------------- .../groovy/groovy/lang/GroovyClassLoader.java | 38 +++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/3341dcd9/src/main/groovy/groovy/lang/GroovyClassLoader.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/GroovyClassLoader.java b/src/main/groovy/groovy/lang/GroovyClassLoader.java index 4b997fe..6c440f9 100644 --- a/src/main/groovy/groovy/lang/GroovyClassLoader.java +++ b/src/main/groovy/groovy/lang/GroovyClassLoader.java @@ -41,6 +41,7 @@ import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.Phases; import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.EncodingGroovyMethods; import org.codehaus.groovy.runtime.IOGroovyMethods; import org.codehaus.groovy.runtime.InvokerHelper; import org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache; @@ -66,6 +67,7 @@ import java.net.URLConnection; import java.net.URLDecoder; import java.security.AccessController; import java.security.CodeSource; +import java.security.NoSuchAlgorithmException; import java.security.Permission; import java.security.PermissionCollection; import java.security.Permissions; @@ -315,8 +317,13 @@ public class GroovyClassLoader extends URLClassLoader { * @return the main class defined in the given script */ public Class parseClass(final GroovyCodeSource codeSource, boolean shouldCacheSource) throws CompilationFailedException { + // it's better to cache class instances by the source code + // GCL will load the unique class instance for the same source code + // and avoid occupying Permanent Area/Metaspace repeatedly + String cacheKey = genSourceCacheKey(codeSource); + return ((ConcurrentCommonCache<String, Class>) sourceCache).getAndPut( - codeSource.getName(), + cacheKey, new EvictableCache.ValueProvider<String, Class>() { @Override public Class provide(String key) { @@ -327,6 +334,35 @@ public class GroovyClassLoader extends URLClassLoader { ); } + private String genSourceCacheKey(GroovyCodeSource codeSource) { + StringBuilder strToDigest; + + String scriptText = codeSource.getScriptText(); + if (null != scriptText) { + strToDigest = new StringBuilder((int) (scriptText.length() * 1.2)); + strToDigest.append("scriptText:").append(scriptText); + + CodeSource cs = codeSource.getCodeSource(); + if (null != cs) { + strToDigest.append("/codeSource:").append(cs); + } + } else { + strToDigest = new StringBuilder(32); + // if the script text is null, i.e. the script content is invalid + // use the name as cache key for the time being to trigger the validation by `groovy.lang.GroovyClassLoader.validate` + // note: the script will not be cached due to the invalid script content, + // so it does not matter even if cache key is not the md5 value of script content + strToDigest.append("name:").append(codeSource.getName()); + } + + try { + return EncodingGroovyMethods.md5(strToDigest); + } catch (NoSuchAlgorithmException e) { + throw new GroovyRuntimeException(e); // should never reach here! + } + } + + private Class doParseClass(GroovyCodeSource codeSource) { validate(codeSource); Class answer; // Was neither already loaded nor compiling, so compile and add to cache.