Java线程堆栈
-
Linux jstack命令
jstack 7756(Java进程号) > java.stack
-
Linux kill命令
kill -3 7756(Java进程号)
向JVM发送QUIT信号
Java线程
-
JVM创建线程
-
用户线程
main函数代表主线程
-
线程堆栈的第一行
1、java线程转换LINUX 内核库pthread即本地线程
2、tid 语言层面的id比如Java 线程编号
3、nid(Native Thread)运行在操作系统基础上的本地id
-
线程调用含义
堆栈信息其中一行的调用关系
-
pstatck 7756(Java进程号)
nid(16进制) == LWP(10进制)
16进制的nid
10进制的LWP(轻量级进程)
锁是否释放
-
wait() 会释放监视锁
-
sleep会占用锁
-
当一个线程占有一个锁时
堆栈会打印 -locked <Oxe7402c48>
-
当该线程等待别的线程释放该锁
堆栈会打印 -waiting to lock <Oxe7402c48>
-
如果代码中有wait调用
首先是locked 然后又会打印 waiting on <Oxe7402c48>
这种会导致死锁即死循环 占用了锁等待其他锁
线程死锁
0时刻 线程0获取锁0 线程1获取锁1
2时刻 线程0获取锁1 线程1获取锁0
两个线程各得到锁 都等待对方的锁
Java堆栈直接给出死锁的结果
死锁报错
死锁详情
死锁情况分析导致CPU过高分析
方法1
步骤
为什么要去掉wait和sleep的线程 因为其不占用cpu资源
原因
方法2 TOP&堆栈方法(一次命中)
JNI是Java Native Interface
5578是java的进程号
打印这个进程中所有线程
cpu消耗倒叙排列 shit+h
jstack把线程堆栈打印出来 里面有很多线程 其中有一个就等于cpu消耗最多的那个pid
就可以直接定位到这行代码有问题
不消耗CPU的线程状态
-
JVM的RUNNABLE状态
网络IO不消耗CPU 是同步阻塞的过程
-
TIMED_WAITING(on object monitor)
obj.wait(time)
-
TIMED_WAITING(sleeping)
Thread.sleep(time)
-
TIMED_WAITING(parking)
被挂起
-
WAITING(on object monitor)
obj.wait()
通过notify()唤醒
-
BLOCKED (on object monitor)
等待监视锁
-
WAITING(parking)
被挂起
Java线程堆栈
-
一次线程堆栈信息
-
多次线程堆栈
资源不足导致性能下降原因
大量线程停在同样的调用上下文中
原因
未及时释放连接的情况
多个锁导致的锁链分析
很多线程在等待不同的锁 有的锁竞争可能由于另外一个锁对象竞争导致 需要找到根源
案例分析
WebAPI性能瓶颈
线上表现
1、连接阻塞比较多RECQ(接收的队列)
2、超时日志疯狂打印
3、通过重启webapi进程临时缓解压力
解决步骤
线程A
线程B
线上故障处理方式
-
保留现场
比如通过jstack保存堆栈快照信息
-
恢复服务
第一时间恢复服务 比如restart、reboot
-
排查解决 -
验证
常规操作
-
重启 -
回滚 -
降级 -
摘机
现象收集&故障定位
潜在bug被激发的情况重启有效
故障排除&服务恢复
确定服务进程排查问题
CPU
top
shift+p 按pid 进程编号升序
shift+m 按占用内存大小降序
内存
free -g
内存
vmstat -n
tcp连接数量
netstat -aonp|grep tcp |wc -l
端口映射
netstat -natp|sort -m
磁盘情况
iostat -d 1
文件占用情况
ls -l /proc/*/fd
目标服务内观察
问题协查方法论
异常处理原则
错误永远无法避免 且发生的角度永远无法预期
在故障发生时尽可能维持系统核心功能的可用性