空心|记 Arthas 实现一次 CPU 排查与代码热更新

1.前言背景
线上代码经常会出现 CPU 占用过高的情况 , 按以往经验我会使用 top 指令 , 进一步借助于 jstack 去查看具体信息从而进行问题排查 , 但基本上都逃不过需要重新发包的局面 , 及时是一个增量包 , 应用也需要短暂停启 。 后来运维大兄弟让我试一下 Arthas , 说是可以进行代码的热更新操作 , 正好来试一下 。
关于 Arthas 的安装与基础使用可以参考下面两篇文章:

  • Arthas 安装与监听 SpringBoot 应用
  • Arthas 基础指令使用说明
环境
JDK1.8SPringBoot 2.2.2ArthasLinux
测试代码:
@RequestMapping(value = "http://kandian.youth.cn/bigThread")@ResponseBodypublic String bigThread(int id) {ArthasService.test();while (true) {Thread t2 = new Thread();t2.start();id ++;if(100000 == id) {return String.valueOf(id);}}}思路
空心|记 Arthas 实现一次 CPU 排查与代码热更新2.thread -b 查看是否有阻塞线程thread -b, 找出当前阻塞其他线程的线程 , 执行完之后并未发现 , 说明该线程并非一直阻塞、一直执行的 。
空心|记 Arthas 实现一次 CPU 排查与代码热更新3.thread 查看占用最高的线程当 thread 之后不跟参数时 , 显示当前全部线程信息 , 我觉得 thread -n 10 , 展示前 10 应该就够用 , 可根据实际需要自己决定 。
下图可以很直观的看出 , 我们的应用瞬间占用了 77% 的 CPU(这里我是发起请求瞬间 , 通过 thread 查看的 , 所以比较直观 , 生产环境应该只有阻塞 , 死锁这种状态才会比较直观) 。
空心|记 Arthas 实现一次 CPU 排查与代码热更新4.thread id 查看具体信息在上一步基础上 , 我们进一步查看 , thread 15(因为上面的 ID=15) 。
空心|记 Arthas 实现一次 CPU 排查与代码热更新【空心|记 Arthas 实现一次 CPU 排查与代码热更新】他的大致意思就是:线程在等待一个条件从而继续执行 , 可以看到方法是在执行 LinkedBlockingQueue.take 方法时候 , 查看这个方法的 API 提示如下:
public E take() throws InterruptedException {E x;int c = -1;final AtomicInteger count = this.count;final ReentrantLock takeLock = this.takeLock;takeLock.lockInterruptibly();try {while (count.get() == 0) {notEmpty.await();}x = dequeue();c = count.getAndDecrement();if (c > 1)notEmpty.signal();} finally {takeLock.unlock();}if (c == capacity)signalNotFull();return x;}其中:AtomicInteger 是保证高并发情况下的原子性 , ReentrantLock 标识可重入锁 , 都是 JUC 包下需要了解的这里不赘述 , 需要的百度了解下 。
这段代码关键点就在于:notEmpty.await() , 从队列中消费数据 , 当队列为空是 , 线程阻塞 , 所以我们大致知道现在出现的问题是线程阻塞 , 但是还是不知道具体哪行代码的问题 。
如果能够明确知道这次更改了哪些代码 , 可以直接执行步骤 6 , 不知道的话可以通过步骤 5 来定位问题 。
5.watch 查看哪个 Controller 执行了代码watch org.springframework.web.servlet.DispatcherServlet getHandler returnObj


推荐阅读