前言在我们的开发过程中,难免会出现所谓的垃圾代码,导致服务器CPU一直处于100%。但是我们的应用已经启动了,导致服务器CPU偏高,但是不知道是哪一边有问题。我们应该如何找出代码的哪一侧有问题?今天老顾就给大家介绍几款快速定位的工具。演示代码先写一些代码,新建一个springbootmaven工程,创建一个web服务,引入SpringBoot内置的web容器,pom.xml关键引用jar包如下:1.``2.`org.springframework.boot`3.`spring-boot-starter-web`4.``创建服务:TestWhile创建Controller:TestWhileprofileapplication.properties1.`server.port=80`2.`server.servlet.context-path=/api`打包项目,上传测试服务器1.`java-jardemo-0.0.1-SNAPSHOT.jar&`打开浏览器,访问无限循环方法打开浏览器,在地址栏输入http://xxxx/api/user/testWhile?size=2,返回“Hello程序执行iscomplete”,说明调用Successfully(开了2个死循环)这个问题代码,已经在服务器上运行了,发现服务器报警了,于是上线查看。native方法这个方法不需要额外安装工具,在没有联网的情况下也可以用这个方法排错比较好g效果。top和printf是Linux原生命令,jstack和jstat是jdk内置的命令工具。许多强大的Linux和java诊断工具也是基于top、jstack和jstat命令封装的。注意:jstack和jstat等命令需要完全安装jdk。linux自带的openJdk一般没有这个工具。可以查看java的bin目录下是否有这些命令。查找消耗CPU最多的进程命令:top–c,显示进程运行信息列表示例:top-c。交互一:按1,数字1,显示多核CPU信息。交互2:输入P(大写p),进程按CPU使用率排序。可以看到双核CPU使用率已经达到了100%。第一个PID为373的进程就是我们要找的罪魁祸首;在进程的最后一栏可以看到进程名,COMMAND注释:“java-jardemo-0.0.1-SNAPSHOT.jar”。找到消耗CPU最多的线程命令:top-H-p[PID],显示某个进程的线程运行信息列表示例:top-Hp373,如下图,可以看到多线程的转换CPU占用率高线程PID为16进制命令:printf"%x\n"[threadpid],将多个线程号转为16进制,在第4步使用前加0x例子:printf'%x\n'406405375376,得到结果196、195、177、178;如下图:查看堆栈,找到线程命令:jstack[processPID]|grep【线程转十六进制】-A10,使用jstack获取进程PID栈,使用grep定位线程id,打印接下来的10行信息。示例:jstack373|grep'0x196'-A10,如下图所示:通过查看堆栈信息,发现问题出在TestWhile.whileTrue;发现有2个GC线程,见上图中的“GC任务线程”#0(ParallelGC)”,代表垃圾回收线程,负责栈的垃圾回收和存储,可以在中查看batches查看堆栈信息,我们也可以换个方式查看,先存储jstack的堆栈信息命令:jstack【进程PID】>【文件】例:jstack373>demo.dump,存储373的堆栈信息然后使用cat+grep查找后面几个高CPU线程的堆栈信息,例子:cat-ndemo.dump|grep-A10'0x196',如下图:可以看到thread0x196[thread196]生成堆栈信息,直接指向方法whileTrue。GC视图,我们可以看到上图中4个线程中有2个没有看到java代码,而是GC任务thread#0(ParallelGC)。这是GC垃圾回收的线程,是死循环导致GC过于频繁,导致高CPU使用率?下面使用jstat查看jvm的GC信息。命令:jstat-gcutil[进程PID][毫秒][打印次数]示例:jstat-gcutil37320005、查看373进程的GC信息,每2秒打印一次,共5次,如图下图:S0:SurvivingZone1当前使用率S1:SurvivingZone2当前使用率E:EdenPark使用率O:OldGeneration使用率M:MetadataZone使用率CCS:压缩使用率YGC:NumberofYoungGenerationGarbageCollectionsFGC:OldGenerationGarbageRecyclingtimesFGCT:oldgenerationgarbagecollection花费的时间GCT:garbagecollection花费的总时间看下Arthas(Alsace)Arthas(Alsace)是java的在线诊断工具,是由阿里巴巴开源,功能非常强大。Arthas支持JDK6+,支持Linux/Mac/Windows,采用命令行交互方式,提供丰富的Tab自动补全功能,进一步方便问题定位和诊断。下面看一下下载Arthas1使用arthas-boot(推荐)下载arthas-boot.jar,然后用java-jar启动:1.`curl-Ohttps://alibaba.github.io/arthas/arthas-boot.jar`2.`java-jararthas-boot.jar`按1进入java进程,此时java进程PID已经变为373进入Alsace完成,如图所示下图,可以看到登录路径变成了[arthas@17376]$,可以进入dashboard进入监控页面。监控视图已经进入了Arthas操作界面,进入dashboard,回车后会看到线程和堆栈信息。如图所示,arthas已经安排了CPU占用率高的线程。上面我们可以看到有2个线程一直处于高位,以及GC的次数和耗时。thread[ID]查看线程ctrl+c退出dashboard界面,输入thread32查看线程信息,如下图:可以看到TestWhile类中whileTrue方法中的put方法导致cpu使用量增加。问题一下子就出来了,Arthas的功能不止于此,可以直接反编译看代码。jad反编译使用Arthas自带的反编译方法jad,输入命令:jadcom.rainbow.demo.service.TestWhile*反编译java类查看问题函数的具体代码,如下图:退出arthas最后,既然问题已经找到,那么就退出Arthas。输入命令:quitArthas的功能很强大,这里简单介绍一下,下次老谷会专门的文章介绍更简单的脚本,马上发现问题show-busy-java-threadsshow-busy-java工具-threads.sh是有用脚本工具集中的工具之一。show-busy-java-threads用于快速排查JavaCPU性能问题(topus值过高),自动找出正在运行的Java进程中消耗CPU较多的线程,并打印出它们的线程栈,从而确定性能问题的方法调用。注意:这个工具的核心还是使用了jdk的jstack方法,只是在上面进行了封装和展示。下载到当前目录下载地址:https://github.com/oldratlee/useful-scripts/releasesbin下有很多工具,我们这次只需要上传show-busy-java-threads.shshow-busy-java-threads脚本到服务器必须要有执行权限chmod+xshow-busy-java-threads直接运行1.`./show-busy-java-threads`如图在吨下图,found可以很快找到CPU占用率最高的前5个线程。从前面两个线程可以看出,和使用原生工具(jstack)看到的是一样的。其他命令show-busy-java-threads和Arthas一样,还有一些其他有用的增强命令:show-busy-java-threads从所有Java进程中找出最消耗CPU的线程(默认5个),打印出它的线程堆栈。show-busy-java-threads-c3-c3:3为n,指定显示消耗cpu最多的前3个线程。show-busy-java-threads-c3-p17376显示进程17376在CPU组中多消耗了3个线程;-p17376:17376为进程PID,-p参数指定进程PID。show-busy-java-threads-s[指定jstack命令的全路径]对于sudo方式的运行,JAVAHOME环境变量不能传给root,root用户往往不配置JAVAHOME,配置不方便,明确指定jstack命令的路径反而更方便。show-busy-java-threads-ayao.log将输出结果导入指定文件yao.log。show-busy-java-threads35每5秒执行一次,一共执行3次;一个省执行一次,默认间隔3秒。总结今天老谷介绍了3种解决服务器CPU负载高的方法。主要流程是:1.检查CPU负载高的进程。2、查看进程中负载高的线程。3、获取进程中的堆栈信息。4.获取栈中对应的线程信息,找到里面的问题方法。在排查过程中,我们不仅使用了原生工具,还使用了增强工具Arthas和show-busy-java-threads,大大简化了我们的排查步骤。其实增强工具无非是对native方法的封装,很多这样的工具都是封装在native方法中的。今天就到这里,谢谢!!!