一次GC线程占用系统CPU过高处理过程

因为测试人员报告说,最近订单系统总是超时,要重启才能恢复正常,但是第二次复现的时间不固定。

然后先是查看下应用状态是否正常,没有问题。

然后查看日志,发现有很多交易报超时。

一次GC线程占用系统CPU过高处理过程_第1张图片

仔细查看超时的日志,发现什么类型的交易都有,而且超时的地方也不固定,更像是系统或者架构层面的错误。

赶紧看下(ps ux)linux系统的状态。发现订单应用的CPU已经100%,但是内存还有剩余,内存占用 大概30%+,下面是已经恢复后的截图。

一次GC线程占用系统CPU过高处理过程_第2张图片

继续查看应用进程中每个线程的具体情况:top -Hp pid

发现有多个线程的CPU特别高,赶紧把堆栈信息打印出来,jstack pid >>pid.stack

用线程的pid转16进制后去里面找,发现是下面这个东西占用高。

一次GC线程占用系统CPU过高处理过程_第3张图片

这个是JVM的GC线程,至此超时的原因找到了,但是引发这个GC线程占用CPU过高的根本原因还没有头绪。

因为这套系统是给外部联调使用,一向很稳定,最近的代码提交日志也没发现啥很特别的地方。查看了下JVM的配置信息,又问了运维人员最近有没有动这套环境,没人动。内存又没溢出,应该不是资源未关闭或者接连未释放。

最后来是来看看堆栈的日志吧。说真的堆栈日志中线程那么多,而且各种线程的堆栈都有,实在不好找。没办法只硬着头皮看看咯。

看了网上堆栈的分析步骤,因为是当时系统还在异常中,可以先从runnable(当前正在执行)状态的线程找。从最后一个开始看,主要是统计哪类线程打印的信息多,发现执行定时任务的线程还比较频繁,赶紧去看定时任务的日志。

发现大量日志报资源繁忙,赶紧停掉定时任务后重启。在此期间密切监视系统资源情况,发现问题还在,CPU的使用率还是在慢慢升高。

没办法,关掉外部接入,不在接受请求了,系统还是没恢复,应该正在执行的某个任务的问题。

还是去看堆栈信息,一步步排查,过了好久。。。。有个特别长的信息引起了我的注意:

一次GC线程占用系统CPU过高处理过程_第4张图片

因为这段代码是业务代码部分的,而且这块代码已经在生产跑了很久没有问题,但是我决定还是看看。重点关注:IO,资源接连,外调请求,各种循环操作。然后有个重大发现。

//每笔最低申购额度不为空时,可申购额度应大于
while( (CommUtil.isNull(minSubsAmount) && CommUtil.compare(applyAmount,new BigDecimal(0))>0) ||(CommUtil.isNotNull(minSubsAmount) && CommUtil.compare(applyAmount,new BigDecimal(minSubsAmount))>=0) ){
						//拆分金额
BigDecimal subOrderAmt = CommUtil.compare(applyAmount,new BigDecimal(maxSubsAmount))>0 ? new BigDecimal(maxSubsAmount) : applyAmount;
FundLimitAllotList fundLimitAllotInfo = SysUtil.getInstance(FundLimitAllotList.class);
fundLimitAllotInfo.setFundCode(fundCode);
fundLimitAllotInfo.setFundName(fundName);
fundLimitAllotInfo.setFundType(null);
fundLimitAllotInfo.setYield(fundInfo.getYield());
fundLimitAllotInfo.setSubOrderAmt(subOrderAmt);//申购金额
fundLimitAllotList.add(fundLimitAllotInfo);
						
applyAmount =applyAmount.subtract(subOrderAmt);//赋值可交易的金额
leftAmount = leftAmount.subtract(subOrderAmt);//剩余待交易金额
}

这个里面有个循环,是用作交易拆笔之用。赶紧去交易日志中看看今天有没有发生这种交易,果然有且最近一笔交易发生的时间也吻合。根据传入的参数对比代码分析,是因为minSubsAmount和applyAmount同时为0时,发生while死循环,不断创建对象,导致GC占用CPU过高。将该处逻辑修复后重新启动,问题解决。

虽然从上面的步骤来看,还算顺利,但其实过程很曲折,因为外部联调环境比较复杂,又很少出错,一直觉得不是代码的问题(最近很少动),围绕着zk,redis,其他系统排查了很久,还关掉一个渠道方的联调交易(该渠道测试交易量比较大)。

这个问题很解决主要还是通过看堆栈信息,这块自己不是很熟悉,所以接下来可能要重点充电了。

PS:CPU过高,内存溢出,请第一时间怀疑代码,先不要尝试通过排查系统参数,JVM配置来解决。

你可能感兴趣的:(linux)