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.

Reply via email to