千家信息网

Groovy脚本引发的 Old GC问题怎么办

发表于:2025-02-12 作者:千家信息网编辑
千家信息网最后更新 2025年02月12日,这篇文章主要为大家展示了"Groovy脚本引发的 Old GC问题怎么办",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"Groovy脚本引发的 Old GC
千家信息网最后更新 2025年02月12日Groovy脚本引发的 Old GC问题怎么办

这篇文章主要为大家展示了"Groovy脚本引发的 Old GC问题怎么办",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"Groovy脚本引发的 Old GC问题怎么办"这篇文章吧。

示例代码如下

ScriptEngineManager factory = new ScriptEngineManager();ScriptEngine engine = factory.getEngineByName("groovy");String function = String.format("def getTargetParamValue(%s) {return \"%s\"}", "o", "$o");engine.eval(function);Invocable invocable = (Invocable) engine;Object result = invocable.invokeFunction("getTargetParamValue", "test-string");System.out.println(result);

这段代码定义了一个Groovy的方法,根据传进去的参数返回对应的值。

由于生产环境流量很大,这段代码被频繁执行。测试时的代码如下

public class ScriptEngineTest {    public static void main(String[] args) {        ScriptEngineManager factory = new ScriptEngineManager();        ScriptEngine engine = factory.getEngineByName("groovy");        //测试时改为死循环        for (int i = 0;; i++) {            try {                String function = String.format("def getTargetParamValue(%s) {return \"%s\"}", "o", "$o");                engine.eval(function);                Invocable invocable = (Invocable) engine;                Object result = invocable.invokeFunction("getTargetParamValue", "test-string");                System.out.println(result);                TimeUnit.MICROSECONDS.sleep(100);                System.out.println(new Date().toLocaleString());            } catch (Exception e) {                String errorMsg = String.format("异常!%s", e.getMessage());                System.out.println(errorMsg);            }        }    }}

模拟生产环境的情况,每秒钟执行10次。通过VusualVM观察JVM

  • CPU使用情况,可以看到在每次堆内存扩容的时候,CPU使用量会有明显增加

  • 堆内存使用情况

  • metaspace使用量一直在增加

  • 类加载情况,total loaded classes一直在增加

  • 线程

  • dump内存

可见,每次循环中生成的 Groovy method在方法执行完成之后并没有被释放掉,导致metaspace的使用量一直增加,最终撑爆JVM

针对以上问题,解决方法为每次将生成的方法缓存下了,下次要执行的时候从缓存中取。

private final ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap<>();private Object getInvokeResult(Object targetParam, String paramName, String expression) throws Exception {    //targetParamClassName="com.umgsai.web.home.vo.NodeVO"    String targetParamClassName = targetParam.getClass().getName();    //expression="$nodeVO.bizOwner"    //paramName="nodeVO"    String functionKey = String.format("%s_%s_%s", targetParamClassName, paramName, expression);    functionKey = StringUtil.replaceChars(functionKey, "$", "");    functionKey = StringUtil.replaceChars(functionKey, ".", "_");    //functionKey为方法的名称和concurrentHashMap的key,这里需要去掉特殊字符    Invocable invocable = concurrentHashMap.get(functionKey);    if (invocable != null) {        //如果缓存中有,直接调用        return invocable.invokeFunction(functionKey, targetParam);    }   //如果缓存中没有,生成方法,并且存到concurrentHashMap    synchronized (lock) {        invocable = concurrentHashMap.get(functionKey);        if (invocable == null) {            String function = String.format("def %s(%s) {return \"%s\"}", functionKey, paramName, expression);            engine.eval(function);            invocable = (Invocable) engine;            concurrentHashMap.put(functionKey, invocable);            if (log.isInfoEnabled()) {                String msg = String.format("Create new Groovy function, functionKey=%s, paramName=%s, expression=%s",                        functionKey,                        paramName, expression);                log.info(msg);            }        }    }    if (log.isInfoEnabled()) {        log.info(String.format("Groovy function concurrentHashMap.size=%d", concurrentHashMap.size()));    }    return invocable.invokeFunction(functionKey, targetParam);}

以上是"Groovy脚本引发的 Old GC问题怎么办"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

0