情景
java程序在使用中出现卡顿,无响应,且刷新无效果
问题原因
可能是程序设计问题,出现死循环(小概率),设计缺陷,死锁
也可能是网络连接问题,需要具体分析
解决办法
使用jstack工具
jstack是JVM自带的Java堆栈跟踪工具,它用于打印出给定的java进程ID、core file、远程调试服务的Java堆栈信息.
这里因为不同的部署情况,有不同的方法来解决问题
docker容器
在出现卡顿时,不要停止docker容器,使用jstack来生成当前程序的快照,来判断当前进行的线程问题。
第一步
连接服务器 输入 top 来查看当前进程
按c 可以查看具体的启动信息和软件包位置
记下 前面的PID ,按q退出top命令
第二步
输入 docker ps 查看容器内程序,找到需要检查的java容器id
第三步
输入 docker exec -it 容器id /bin/sh 进入到容器id 的/bin/sh中
第四步
jstack 进程id >输出文件名称
1.
到这里可以直接 cat 文件名称,直接查看,但是一般文件较长不易查询问题可以继续进行
2.
还有另一种解决办法,修改docker的启动配置,将主机文件挂载到容器上,这样生成的文件直接就会在主机对应目录中,不需要再拷贝
- /path/on/host:/path/on/container
`` 上述Compose文件将主机上的
/path/on/host目录挂载到容器的
/path/on/container`目录。
第五步(非必要)
exit 退出docker
docker cp 容器id(或名称):文件地址 主机目录 --将容器 的 目录(文件)拷贝到主机的目录中。
java程序
- top
- top -Hp pid 得到线程id
- jstack pid
- jstack -l [PID] >/tmp/log.txt
- 分析堆栈信息
在 jstack.log
中查找对应的十六进制线程 ID,分析该线程的堆栈信息,定位高 CPU 占用的代码
分析文件
- 执行
jstack
命令,将得到进程的堆栈信息。一般使用jstack -l pid
来得到长列表,显示其详细信息。 - 有时线程挂起的时候,需要执行
jstack -F pid
来获取。 - 在实际运行中,往往一次 dump的信息,还不足以确认问题。建议产生三次 dump信息,如果每次 dump都指向同一个问题,我们才确定问题的典型性。
- 堆栈信息只是一种参考,一些正常RUNNING的线程,由于复杂网络环境和IO的影响,也是有问题的,用jstack就无法定位,需要结合对业务代码的理解。
在dump 文件里,写法可能不太一样:
- 死锁,Deadlock(重点关注)
- 执行中,Runnable
- 等待资源,Waiting on condition(重点关注)
- 等待获取监视器,Waiting on monitor entry(重点关注)
- 对象等待中,Object.wait() 或 TIMED_WAITING
- 暂停,Suspended
- 阻塞,Blocked(重点关注)
- 停止,Parked
Blocked
线程阻塞,是指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。
如果线程处于Blocked状态,但是原因不清楚。可以使用jstack -m pid
得到线程的mixed信息。
0xff31e8b8 ___lwp_cond_wait + 0x4
0xfea8c810 void ObjectMonitor::EnterI(Thread*) + 0x2b8
0xfeac86b8 void ObjectMonitor::enter2(Thread*) + 0x250
例如,以上信息表明,线程在尝试进入同步块时阻塞了。