JVM由多个线程组成了一个进程,每个线程代表一个操作。线程信息主要用于分析当时整个服务器正在执行什么操作。
线程信息主要用于分析服务器宕机(因为假如服务器宕机,从线程可能可以看到死锁,某操作一直执行没有完成,如数据库查询语句没有返回)。
或某些操作服务器一直不响应的情况,如保存操作一直没有响应,查询报表没有响应之类的问题都可以通过线程看出来。
在系统没有宕机时,可以访问"系统监控"->"线程"获取线程信息
V62或V72以后的版本也可以访问应用服务器smartbi\mlogs-smartbi\threaddump目录获取最新的线程信息
注:之所以会有上面的线程信息,是因为默认启用自动打印线程信息,可以在系统选项中修改,见下图:
要求JDK版本为1.6及其以上版本。
1、在运行中打开cmd命令行窗口。
2、在cmd窗口进入JDK的bin目录下,执行jps获取进程信息,此处要保证执行的JDK是服务器使用的JDK。
如命令行:cd C:\Smartbi_Insight\jdk\bin
jps
3、获取对应线程号,然后执行jstack +进程号 > 进程号.log 获取线程信息 :Tomcat显示的名称应该是Bootstrap;
4、可以在 C:\Smartbi_Insight\jdk\bin 目录下看到生成的线程文件。
1、通过CMD命令行窗口进入JDK的bin目录下
2、使用netstat -ano|find "访问的端口号"|find "LISTENING" 获取进程号(注意双引号是必须的)
如访问smartbi的端口号是18080,则输入命令:netstat -ano|find "18080"|find "LISTENING"
3、再使用jstack 进程号 >进程号.log 获取线程信息,如果提示无法连接到JVM,可以增加-F参数强制生成jstack -F 进程号 >进程号.log
上边截图获取到进程号是7280,可输入命令行:jstack 7280 >7280.log
或者:输入命令行:jstack -F 7280 >test.log
1、进入jdk/bin,执行jvisualvm.exe
2、找到对应的线程号,然后进入线程 --> 线程 dump
Lunix下通过ps ef|grep java获取线程号,再通过kill -3 pid获取线程信息
在线程文件中查找smartbi字样, 查看完整的线程信息分析。
线程的状态有BLOCK,WAITING,RUNNABLE
BLOCK是等待锁的线程(代码里含有synchronized),需要看该线程等待的线程在执行什么操作,如果持有锁的线程处于RUNNABLE则是正常行为(某些情况长时间RUNNABLE也是不正常行为,如执行一个简单sql数据库没有响应),如果处于BLOCk,要继续查询下个锁的进程, 通常就是两种结果RUNNABLE和死锁。
Waiting是等待别人唤醒的线程,代码里主动调用了wait() 引起的, 需要notify()唤醒,在smartbi产品的场景里,主要有池、电子表格报表执行, 比如,数据库连接池满了,新的获取数据库链接的线程会长时间处于waiting状态,直到现有使用连接池的数据库链接关闭,会唤醒其中的waiting线程继续执行,当线程长时间处于waiting状态,可能是需要重点关注分析的(见下文)。
RUNNABLE是正在执行的线程。
有时全部线程都处于RUNNABL状态,需要多次打印比较线程的执行状态, 如果通过这多次打印结果看到某个线程一直执行同一操作,如一直读取socket,一直在删除文件。
一般拿到线程信息,优先关注smartbi的block线程,block线程没问题时,这时通常一个个看,主要是看每个线程在干什么,通过这个干什么判断一些问题,这时候考验对产品的熟悉度。下面是常见的案例:
通常lock是synchronized产生的,我们需要查找处于block状态的线程,
搜索locked <0x00000000eacb6600>(这个代表持有这个锁)
找到对应的持有锁线程,查看其执行逻辑(这时候靠人为判断逻辑有没有问题)
"http-bio-8072-exec-9" daemon prio=6 tid=0x0000000010e37800 nid=0x238c waiting on condition [0x000000001485c000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
locked <0x00000000eacb6600> (a smartbi.freequery.report.SimpleReportBO)
线程信息中出现大量在执行含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 <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)
这时在监控界面看到连接池active的个数和数据源设置的大小是一样的。
这时需要在系统一重启时,在"系统运维">"调试工具集">" 连接池信息"启动跟踪。
连接数据到一半或以上时刷新页面分析获取数据库连接的代码来源。
线程中出现如下内容时通常是对象池被使用满了
at java.lang.Object.wait(Native Method)
locked <0x00000000c396d7b8> (a smartbi.pool.BusinessViewBOPool)
这时需要在系统一重启时,在"系统运维">"调试工具集">" 对象池信息"启动跟踪。
对象池个数到一半或以上时刷新页面分析获取对象池的代码来源。
当线程出现相互等待时,就是代码出现死锁了
"pool-2-thread-2":
at smartbi.xxx(xxx.java:1)
locked < lockid1> (a smartbi.xxx)
这找到对应代码查看其synchronized对象是否一致。 以下是可以产生死锁的代码在采用了上面方法,都没确认问题时,接下来只能一一查看每个线程的具体执行逻辑内容,判断其正在处理的逻辑,这需要熟悉代码。
附上线程分析的工具ThreadAnalyzer的使用帮助,点击其中的常用工具>ThreadAnalyzer,该工具可以很方便的看出锁等待及死锁问题。
请使用ThreadAnalyzer工具分下下面两个案例原因,提示:其中一个是卡在数据库连接不返回,另一个主要是集群缓存同步缓慢。