|
当运行多个程序或一个程序并发运行多个任务时,系统需要分配更多的处理器资源,导致 CPU 占用率上升。
通常来讲,cpu 占用很高属于正常现象,因为程序需要多并发访问时,会有多线程处理任务,每一个线程都有可能占用着一个,当运行多个程序或一个程序并发运行多个任务时,系统需要分配更多的处理器资源,导致 CPU 占用率上升。
在线程处理完计算会及时释放不占用 cpu 的,所以 cpu 有波动,时高时低是正常的。
一、先区分:CPU 高是(正常波动)还是(异常持续高)
这是排查的前提,避免无效操作:
在服务器上通过top -c查看系统CPU占用,按CPU使用率进行排序,重点关注COMMAND列为java的进程。

- 正常情况:SmartBI 报表并发计算、大数据量导出、定时缓存刷新时,多线程占用 CPU 做运算,会出现 CPU 临时冲高,任务完成后线程释放 CPU,占用率回落,时高时低的波动属于业务正常消耗。
- 异常情况:CPU 占用率长时间居高不下(如 8 核服务器 CPU 占比持续 700%+、16 核持续 1500%+),且业务操作卡顿 / 系统无法访问,需立即排查高 CPU 线程。
二、快捷排查(系统可访问时优先用)
在出现异常情况下,若 SmartBI 应用还能正常访问,无需登录服务器,直接通过产品内置监控页面快速定位高 CPU 线程。
步骤最简:访问地址:http://服务器IP:Tomcat端口/smartbi/vision/monitor/listthreads.jsp
该页面直接展示 JVM 内所有线程的运行状态、CPU 占用、执行栈信息,可直接筛选CPU 占比高的线程,查看其具体执行的业务代码 / 操作(如某张报表计算、某类导出任务)
条件允许的情况下,可将页面保存,录制CPU采样,必要时录制charles辅助分析,一起打包发回。
收集信息参考地址

三、服务器排查(系统无法访问时使用)
当 SmartBI 页面无法打开 / 卡顿严重,需登录 Linux 服务器通过命令行排查,通过指令查找高CPU的Java 进程解析线程定位原因
步骤 1:通过 top 命令定位高CPU的Java进程:top -c
如出现 java 进程CPU占比 持续不降低,并且该进程即为 SmartBI 部署的Tomcat 主进程,记录该PID(后续所有操作基于此 PID)。

然后通过命令
jstack 7299 > smartbi_thread.log # 7299是Tomcat的进程PID
这一步会把进程内所有线程信息都导出到 smartbi_threads.log 里
步骤 2:查看进程内高CPU线程,获取线程PID(TID)
注:一个线程最多占用一个cpu,即占用最高100%。
以上图为例,如出现CPU一直居高不下
这个时候可以通过top -H -p 7299 输出对应进程下哪些线程占用cpu较高,如下图:

步骤 3:十进制PID转换为十六进制
Jstack 工具解析线程栈时,线程 ID 为十六进制小写格式,需做进制转换,两种方式任选:
通过命令:printf "%x\n" 21102
输出十六进制结果,得到对应16进制为 526e

再根据十六进制 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 thread、ParNew、ConcurrentMarkSweep的线程,且状态均为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。
文章到这里结束啦,但是我们可以做个题再来巩固下知识库,答题可领取麦豆哦—>点击领取任务 |