Sfoglia il codice sorgente

Merge pull request #677 from soenter/master

增加class的加载缓存,解决频繁加载class会使jvm的方法区空间不足导致OOM的问题
许雪里 6 anni fa
parent
commit
3e9fe7baa4

+ 1 - 1
xxl-job-core/src/main/java/com/xxl/job/core/biz/impl/ExecutorBizImpl.java

@@ -113,7 +113,7 @@ public class ExecutorBizImpl implements ExecutorBiz {
             // valid handler
             if (jobHandler == null) {
                 try {
-                    IJobHandler originJobHandler = GlueFactory.getInstance().loadNewInstance(triggerParam.getGlueSource());
+                    IJobHandler originJobHandler = GlueFactory.getInstance().loadNewInstance(triggerParam.getJobId(), triggerParam.getGlueSource());
                     jobHandler = new GlueJobHandler(originJobHandler, triggerParam.getGlueUpdatetime());
                 } catch (Exception e) {
                     logger.error(e.getMessage(), e);

+ 32 - 2
xxl-job-core/src/main/java/com/xxl/job/core/glue/GlueFactory.java

@@ -4,6 +4,10 @@ import com.xxl.job.core.glue.impl.SpringGlueFactory;
 import com.xxl.job.core.handler.IJobHandler;
 import groovy.lang.GroovyClassLoader;
 
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.util.concurrent.ConcurrentHashMap;
+
 /**
  * glue factory, product class/object by name
  *
@@ -30,6 +34,8 @@ public class GlueFactory {
 	 */
 	private GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
 
+	private static final ConcurrentHashMap<String, Class<?>> CLASS_CACHE = new ConcurrentHashMap<>();
+	private static final ConcurrentHashMap<Long, String> JOBID_MD5KEY_CACHE = new ConcurrentHashMap<>();
 
 	/**
 	 * load new instance, prototype
@@ -38,9 +44,9 @@ public class GlueFactory {
 	 * @return
 	 * @throws Exception
 	 */
-	public IJobHandler loadNewInstance(String codeSource) throws Exception{
+	public IJobHandler loadNewInstance(long jobId, String codeSource) throws Exception{
 		if (codeSource!=null && codeSource.trim().length()>0) {
-			Class<?> clazz = groovyClassLoader.parseClass(codeSource);
+			Class<?> clazz = getCodeSourceClass(jobId, codeSource);
 			if (clazz != null) {
 				Object instance = clazz.newInstance();
 				if (instance!=null) {
@@ -66,4 +72,28 @@ public class GlueFactory {
 		// do something
 	}
 
+	private Class<?> getCodeSourceClass(long jobId, String codeSource){
+		try {
+			MessageDigest md = MessageDigest.getInstance("MD5");
+			byte[] md5 = md.digest(codeSource.getBytes());
+			BigInteger no = new BigInteger(1, md5);
+			String md5Str = no.toString(16);
+			Class<?> clazz = CLASS_CACHE.get(md5Str);
+			if(clazz == null){
+				clazz = groovyClassLoader.parseClass(codeSource);
+				Class<?> preClazz = CLASS_CACHE.putIfAbsent(md5Str, clazz);
+
+				// 如果代碼有變化則刪除之前class緩存
+				if(preClazz == null){
+					String preMd5 = JOBID_MD5KEY_CACHE.put(jobId, md5Str);
+					if(preMd5 != null){
+						CLASS_CACHE.remove(preMd5);
+					}
+				}
+			}
+			return clazz;
+		} catch (Exception e) {
+			return groovyClassLoader.parseClass(codeSource);
+		}
+	}
 }