线上CPU高问题排查与解决

本文介绍了在新工作中遇到的定时重启服务中,通过`jstat`, `jmap`, 和`Groovy`代码分析解决无限GC问题的过程,重点在于定位到Groovy代码,并提供了GroovyCompiler和ScriptExecuter类的示例。最后提到正确使用线程池的重要性。

背景:

最近新入职一家公司,多个服务需要每天定时重启才能保证正常使用。一直没有人解决(搞不懂为啥不解决)

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其实很好用,一个字爽

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

封于修讨教了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值