背景:
最近新入职一家公司,多个服务需要每天定时重启才能保证正常使用。一直没有人解决(搞不懂为啥不解决)
1、top命令或者其他找出占用cpu top pid
2、jstat pid 打印堆栈
3、pid转16进制,去生成的文件搜索
结果是gc线程
jstat -gc 12345 + 时间间隔
无限gc
4、jmap -dump:format=b,file=/xxx/dump.hprof pid 或者其他格式都可
5、下载文件用visualVM打开分析
然后定位到是groovy部分的问题,说实话这东西不够直观,全屏直觉(哈哈)
问题代码
解决方案:个人对groovy比较熟
=========================================================================
jfinal框架,spring可以用ioc import groovy.lang.GroovyClassLoader; import groovy.lang.Script; /** * @author Jianwu Wang * @dateTime 2022/5/20 */ public interface GroovyCompiler { default Class<Script> compile(String script){ try(GroovyClassLoader groovyLoader = new GroovyClassLoader()){ return groovyLoader.parseClass(script); } catch (Exception e) { throw new RuntimeException("脚本编译异常.",e); } } }
===================================================================
import com.jfinal.aop.Aop; import groovy.lang.Script; import org.apache.commons.codec.digest.DigestUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Jianwu Wang * @dateTime 2022/5/20 */ public class PreCompile implements GroovyCompiler { private static final Logger logger = LoggerFactory.getLogger(PreCompile.class); private ScriptMetaSpace scriptMetaSpace = Aop.get(ScriptMetaSpace.class); public Class<Script> compileScript(String script, Integer id) { //key方便清除脚本信息 String key = id + "_" + getRefName(script); //map存在,则获取直接返回 if (scriptMetaSpace.containsKey(key)) { return scriptMetaSpace.get(key); } //不存在则编译,放入map logger.debug("groovy script[{}]", script); Class<Script> scriptClass = compile(script); scriptMetaSpace.put(key, scriptClass); return scriptClass; } private String getRefName(String script) { return DigestUtils.md5Hex(script); } }
import com.jfinal.aop.Aop; import groovy.lang.Binding; import groovy.lang.Script; import org.apache.commons.lang.StringUtils; /** * @author Jianwu Wang * @dateTime 2022/5/20 */ public class ScriptExecuter { private PreCompile preCompile = Aop.get(PreCompile.class); public Object execute(String script, Binding binding,Integer id){ if (StringUtils.isEmpty(script)){ return null; } Class<Script> clazz = preCompile.compileScript(script,id); try { Script groovyScript = clazz.newInstance(); groovyScript.setBinding(binding); return groovyScript.run(); } catch (Exception e) { throw new RuntimeException("执行groovy脚本异常.",e); } } } import groovy.lang.Script; import java.util.concurrent.ConcurrentHashMap; /** * @author Jianwu Wang * @dateTime 2022/5/20 */ public class ScriptMetaSpace extends ConcurrentHashMap<String, Class<Script>> { public ScriptMetaSpace(){ super(); } }
调用:
最后别忘了变更后的移除
发版以后问题确实得到了解决。
后续用mat看了下我的直觉没错(0.0)
还有一个项目也是如此分析解决的
原因线程池、jvm锁使用不当,一定要手动创建线程池哦。 结尾分享下,大家吐槽的Optional其实很好用,一个字爽