利用阿尔萨斯精准定位Java应用CPU负载过高问题

  

阿尔萨斯官方社区正在举行征文活动,参加即有奖品拿哦~   点击投稿

  

作者|张云翔

  

最近我们线上有个应用服务器有点上头,CPU总能跑到99%,我寻思着它流量也不大啊,为啥能把自己整这么累?于是我登上这台服务器,看看它到底在干啥!

  

以前碰到类似问题,可能会考虑使用   <代码>前惠普> jstack 命令去排查,虽然能大致定位到问题范围,但有效信息还是太少了,多数时候还是要靠猜。今天向大家推荐一款更高效更精准的工具:   <代码>阿尔萨斯>   

这次我们利用它来排查CPU负载高的问题.CPU负载过高一般是某个或某几个线程有问题,所以我们尝试使用第一个命令:> <代码>   <前>   <代码> [arthas@384]美元线程   新线程总数:112:0,可运行:26日,阻塞:0,等待:31日TIMED_WAITING: 55,终止:0   ID名称% CPU时间   108 h . .ec-0 RUNNABLE 51 4011:48   100 h . .ec RUNNABLE 48 4011:51   …      

  

为了方便阅读,删掉了一些不重要的信息

     

可以看到、CPU资源几乎被前两个线程占满,并且已经执行了4000多分钟,我们服务器也就启动了两天,可见这两天它们是一刻也没闲着!那它们究竟在干什么呢?我们可以使用命令:   <代码>线程id>   <前>   <代码> arthas@384线程108美元   “http - nio - 7001 - exec - 10“RUNNABLE Id=108 cpuUsage=51%   c.g.c.c.HashBiMap.seekByKey (HashBiMap.java)   c.g.c.c.HashBiMap.put (HashBiMap.java: 270)   c.g.c.c.HashBiMap.forcePut (HashBiMap.java: 263)   c.y.r.j.o.OaInfoManager.syncUserCache (OaInfoManager.java: 159)      

  

也可以使用线程- n 3命令打印出CPU占比最高的前三个线程,这差不多是在   <代码>前惠普> printf> jstack>      

可以看的到,这个线程一直在执行   <代码> HashBiMap。seekByKey 方法(可以重复执行几次   <代码>线程id 确保该线程执行的方法没有时刻在变化),造成这个问题一般有两个原因:

  
      <李>   <代码> seekByKey 方法被循环调用李   <李>   <代码> seekByKey>
  

先看一下是不是第一种,我们使用tt命令监听一下这个方法的调用情况:

  <前>   100年<代码> tt - t com.google.common.collect.HashBiMap seekByKey - n      

注意:在线上执行这个命令的时候,一定要记得加上- n参数,否则线上巨大的流量可能会瞬间撑爆你的JVM内存执行结果显示,   <代码> seekByKey 方法并没有被一直调用,那大概率是   <代码> seekByKey 方法内部有死循环。看下这个方法内部的逻辑,我们可以使用   <代码> jad com.google.common.collect.HashBiMap seekByKey 命令反编译这个方法,这样做的好处是显得比较高端,不过我还是打算直接找到源码,说不定还有注释。源码如下:

  <前>   <代码>私人BiEntryseekByKey (@Nullable对象键,int keyHash) {   (BiEntry输入=hashTableKToV [keyHash,面具];   入口!=零;   输入=entry.nextInKToVBucket) {   如果(keyHash==条目。keyHash,,对象。平等(关键,entry.key)) {   返回条目;   }   }   返回null;   }      

然后并没有注释,还好这个方法逻辑比较简单,也很容易看懂。

  
      <李>通过哈希找到桶中,每个桶是一个链表,李   <李>遍历链表,找到这个关键对应的条目。这里要留意下输入的下一个节点是nextInKToVBucket,后文中会用的到。
  

发生了死循环,我们猜想可能是因为这个链表有环路。那么有没有办法验证这个猜想呢?答案是有!那么如何验证呢?首先我们要获得这个   <代码> HashBiMap> 利用阿尔萨斯精准定位Java应用CPU负载过高问题