线程信息的作用
JVM由多个线程组成了一个进程,每个线程代表一个操作。线程信息主要用于分析当时整个服务器正在执行什么操作。
线程信息的使用场景
线程信息主要用于分析服务器宕机(因为假如服务器宕机,从线程可能可以看到死锁,某操作一直执行没有完成,如数据库查询语句没有返回)。
或某些操作服务器一直不响应的情况,如保存操作一直没有响应,查询报表没有响应之类的问题都可以通过线程看出来。
怎么打印线程信息
方法1
在系统没有宕机时,可以访问"系统监控"->"线程"获取线程信息
方法2
V62或V72以后的版本也可以访问应用服务器smartbi\mlogs-smartbi\threaddump目录获取最新的线程信息
注:之所以会有上面的线程信息,是因为默认启用自动打印线程信息,可以在系统选项中修改,见下图:
方法3
要求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 目录下看到生成的线程文件。
方法4
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
方法5
1、进入jdk/bin,执行jvisualvm.exe
2、找到对应的线程号,然后进入线程 --> 线程 dump
方法6
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线程没问题时,这时通常一个个看,主要是看每个线程在干什么,通过这个干什么判断一些问题,这时候考验对产品的熟悉度。下面是常见的案例:
1.线程等待
通常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)
- parking to wait for <0x00000000ec01f3a8> (a java.util.concurrent.FutureTask$Sync)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos(AbstractQueuedSynchronizer.java:1011)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(AbstractQueuedSynchronizer.java:1303)
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:227)
at java.util.concurrent.FutureTask.get(FutureTask.java:91)
at smartbi.connectionpool.ConnectionPool$7.validateConnection(ConnectionPool.java:662)
at org.apache.commons.dbcp.PoolableConnectionFactory.validateObject(PoolableConnectionFactory.java:308)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:855) - locked <0x00000000ca80cae0> (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 <0x00000000ebffd8b0> (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)
at smartbi.freequery.report.SimpleReportBO.getGridDataInternal(SimpleReportBO.java:1005) - locked <0x00000000eacb6600> (a smartbi.freequery.report.SimpleReportBO)
at smartbi.freequery.report.SimpleReportBO.getGridData(SimpleReportBO.java:981) locked <0x00000000eacb6600> (a smartbi.freequery.report.SimpleReportBO)
2.连接池满
线程信息中出现大量在执行含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)
这时在监控界面看到连接池active的个数和数据源设置的大小是一样的。
这时需要在系统一重启时,在"系统运维">"调试工具集">" 连接池信息"启动跟踪。
连接数据到一半或以上时刷新页面分析获取数据库连接的代码来源。
3.对象池满
线程中出现如下内容时通常是对象池被使用满了
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000c396d7b8> (a smartbi.pool.BusinessViewBOPool)
at java.lang.Object.wait(Object.java:485)
at smartbi.pool.GenericKeyedObjectPool.borrowObject(GenericKeyedObjectPool.java:839) - locked <0x00000000c396d7b8> (a smartbi.pool.BusinessViewBOPool)
at smartbi.pool.BaseSmartbiObjectPool.borrowObject(BaseSmartbiObjectPool.java:148) - locked <0x00000000c396d7b8> (a smartbi.pool.BusinessViewBOPool)
at smartbi.pool.BusinessViewBOPool.borrowObject(BusinessViewBOPool.java:47) locked <0x00000000c396d7b8> (a smartbi.pool.BusinessViewBOPool)
这时需要在系统一重启时,在"系统运维">"调试工具集">" 对象池信息"启动跟踪。
对象池个数到一半或以上时刷新页面分析获取对象池的代码来源。
4.死锁
当线程出现相互等待时,就是代码出现死锁了
"pool-2-thread-2":
at smartbi.xxx(xxx.java:1)
- waiting to lock <lockid1> (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 <lockid2> (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) { }
}
}
}5.其他问题
在采用了上面方法,都没确认问题时,接下来只能一一查看每个线程的具体执行逻辑内容,判断其正在处理的逻辑,这需要熟悉代码。
附上线程分析的工具ThreadAnalyzer的使用帮助,点击其中的常用工具>ThreadAnalyzer