麦粉社区
>
帖子详情

[系统运维] 突发 CPU 飙高?快速揪出异常线程

动态中心 发表于 9 小时前
发表于 9 小时前

当运行多个程序或一个程序并发运行多个任务时,系统需要分配更多的处理器资源,导致 CPU 占用率上升。


通常来讲,cpu 占用很高属于正常现象,因为程序需要多并发访问时,会有多线程处理任务,每一个线程都有可能占用着一个,当运行多个程序或一个程序并发运行多个任务时,系统需要分配更多的处理器资源,导致 CPU 占用率上升。


在线程处理完计算会及时释放不占用 cpu 的,所以 cpu 有波动,时高时低是正常的。


 


一、先区分:CPU 高是(正常波动)还是(异常持续高)


这是排查的前提,避免无效操作:


在服务器上通过top -c查看系统CPU占用,按CPU使用率进行排序,重点关注COMMAND列为java的进程。


 


IMG_256


 



  1. 正常情况:SmartBI 报表并发计算、大数据量导出、定时缓存刷新时,多线程占用 CPU 做运算,会出现 CPU 临时冲高,任务完成后线程释放 CPU,占用率回落,时高时低的波动属于业务正常消耗。

  2. 异常情况:CPU 占用率长时间居高不下(如 8 核服务器 CPU 占比持续 700%+、16 核持续 1500%+),且业务操作卡顿 / 系统无法访问,需立即排查高 CPU 线程。


 


二、快捷排查(系统可访问时优先用)


在出现异常情况下,若 SmartBI 应用还能正常访问,无需登录服务器,直接通过产品内置监控页面快速定位高 CPU 线程。


步骤最简:访问地址:http://服务器IP:Tomcat端口/smartbi/vision/monitor/listthreads.jsp


该页面直接展示 JVM 内所有线程的运行状态、CPU 占用、执行栈信息,可直接筛选CPU 占比高的线程,查看其具体执行的业务代码 / 操作(如某张报表计算、某类导出任务)


条件允许的情况下,可将页面保存,录制CPU采样,必要时录制charles辅助分析,一起打包发回。


收集信息参考地址


 


IMG_257


 


三、服务器排查(系统无法访问时使用)


当 SmartBI 页面无法打开 / 卡顿严重,需登录 Linux 服务器通过命令行排查,通过指令查找高CPU的Java 进程解析线程定位原因


 


步骤 1:通过 top 命令定位高CPU的Java进程:top -c


如出现  java 进程CPU占比 持续不降低,并且该进程即为 SmartBI 部署的Tomcat 主进程,记录该PID(后续所有操作基于此 PID)。


 


IMG_258


 


然后通过命令


jstack 7299 > smartbi_thread.log # 7299是Tomcat的进程PID


这一步会把进程内所有线程信息都导出到 smartbi_threads.log 里


 


步骤 2:查看进程内高CPU线程,获取线程PID(TID)


注:一个线程最多占用一个cpu,即占用最高100%。


以上图为例,如出现CPU一直居高不下


这个时候可以通过top -H -p 7299 输出对应进程下哪些线程占用cpu较高,如下图:


 


IMG_259


 


步骤 3:十进制PID转换为十六进制


Jstack 工具解析线程栈时,线程 ID 为十六进制小写格式,需做进制转换,两种方式任选:


通过命令:printf "%x\n" 21102


输出十六进制结果,得到对应16进制为 526e


 


IMG_260


 


再根据十六进制 PID打开步骤2中打印的线程文件里,根据 526e 忽略大小写搜索,可以找到nid为0x2b4516的线程,就能知道对应线程在做啥了。


  


四、怎么分析线程信息


在线程文件中查找smartbi字样, 查看完整的线程信息分析。


线程的状态有BLOCK,WAITING,RUNNABLE



  • BLOCK是等待锁的线程(代码里含有synchronized),需要看该线程等待的线程在执行什么操作,如果持有锁的线程处于RUNNABLE则是正常行为(某些情况长时间RUNNABLE也是不正常行为,如执行一个简单sql数据库没有响应),如果处于BLOCk,要继续查询下个锁的进程, 通常就是两种结果RUNNABLE和死锁。

  • Waiting是等待别人唤醒的线程,代码里主动调用了wait() 引起的, 需要notify()唤醒,在smartbi产品的场景里,主要有池、电子表格报表执行, 比如,数据库连接池满了,新的获取数据库链接的线程会长时间处于waiting状态,直到现有使用连接池的数据库链接关闭,会唤醒其中的waiting线程继续执行,当线程长时间处于waiting状态,可能是需要重点关注分析的(见下文)。

  • RUNNABLE是正在执行的线程。有时全部线程都处于RUNNABL状态,需要多次打印比较线程的执行状态, 如果通过这多次打印结果看到某个线程一直执行同一操作,如一直读取socket,一直在删除文件。
    一般拿到线程信息,优先关注smartbi的block线程,block线程没问题时,这时通常一个个看,主要是看每个线程在干什么,通过这个干什么判断一些问题


下面是常见的案例:


 


1:GC频繁导致CPU过高


在线程文件中搜索到大量包含GC task threadParNewConcurrentMarkSweep的线程,且状态均为RUNNABLE,基本可判定为 GC 频繁导致CPU高占。


示例:"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f3e6800b800 nid=0x2b4516 runnable


 


2:死锁


当线程出现相互等待时,就是代码出现死锁了


"pool-2-thread-2":

at smartbi.xxx(xxx.java:1)

waiting to lock  (a smartbi.xxx)

at smartbi.xxx(xxxxx.java:138)

locked < lockid2> (a smartbi.xxx)

"pool-3-thread-3":

at smartbi.xxx(xxx.java:999)

waiting to lock  (a smartbi.xxx)

at smartbi.xxx(xxxxx.java:234)

locked < lockid1> (a smartbi.xxx)

这找到对应代码查看其synchronized对象是否一致。 以下是可以产生死锁的代码 


Class A { 

        Void a() {

           Synchronized(B.class) {

              Synchronized(C.class) { }

            }

        }

       Void b() {

              Synchronized(C.class) {

                   Synchronized(B.class) { }

            }

       }

}

 


3:连接池满


线程信息中出现大量在执行含GenericObjectPool.borrowObject的线程的(意味着好多操作在等待获取连接),代表连接池满了,具体的线程信息类似如下:


"http-bio-8072-exec-41" daemon prio=6 tid=0x0000000010600000 nid=0x3588 in Object.wait() [0x0000000020f8d000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

at java.lang.Object.wait(Object.java:485)

at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:810)

locked <0x00000000f93cdf60> (a smartbi.connectionpool.ConnectionPool$5)

at smartbi.connectionpool.ConnectionPool$5.borrowObject(ConnectionPool.java:504)

at org.apache.commons.dbcp.PoolingDriver.connect(PoolingDriver.java:175)

at smartbi.connectionpool.ConnectionPool.driverConnect(ConnectionPool.java:216)

at smartbi.connectionpool.ConnectionPool.getConnection(ConnectionPool.java:354)

at smartbi.freequery.querydata.store.DBSQLResultStore.executeInDatabase(DBSQLResultStore.java:1343)

locked <0x00000000f4ae85e0> (a smartbi.freequery.querydata.store.DBSQLResultStore)

at smartbi.freequery.querydata.store.DBSQLResultStore.ensureGridDataInMemDB(DBSQLResultStore.java:4422)

at smartbi.freequery.querydata.store.DBSQLResultStore.getGridDataInternal(DBSQLResultStore.java:3825)

at smartbi.freequery.querydata.store.SQLResultStore.getGridData(SQLResultStore.java:156)

PS:当然这只是部分常见问题,遇到比较复杂,条件允许的情况下,也可直接参考wiki打印线程信息和dump文件一起打包发回进行排查


https://wiki.smartbi.com.cn/pages/viewpage.action?pageId=101876306


 


五、线程分析工具


JVisualVM


JVisualVM是集成了多个JDK命令工具的一个可视化工具,它主要用来监控JVM的运行情况,可以用它来查看和浏览Heap Dump、Thread Dump、内存对象实例情况、GC执行情况、CPU消耗以及类的装载情况。详细请见:JVisualVM


 


ThreadAnalyzer


一种可以识别java线程中挂起、死锁、资源争用,瓶颈等的线程查看工具,具体帮助信息请见:http://www-01.ibm.com/support/docview.wss?uid=swg27011855&aid=1


 


 


文章到这里结束啦,但是我们可以做个题再来巩固下知识库,答题可领取麦豆哦—>点击领取任务

高级模式
B Color Image Link Quote Code Smilies
您需要登录后才可以回帖 登录 | 立即注册

0回帖数 0关注人数 61浏览人数
最后回复于:9 小时前

社区

指南

AI

搜索

快速回复 返回顶部 返回列表