游戏卡顿白皮书 › L9 服务器游戏进程
死锁 Deadlock
原因 ID sp-deadlock · 主责 研发团队·服务器开发
在含图示和实验的完整版中打开此卡片 →
两个线程互相等待对方持有的锁,就会永远停住。
起因 线程 A 拿着锁 1 等锁 2,B 拿着锁 2 等锁 1 → 结果 两者都永远停住,相关线程也一个接一个停住 → 画面表现 整台服务器停住,看门狗重启服务器,所有人掉线
- 症状
- 卡住, 掉线
- 因素
- 停顿
- 谁会遇到
- 全服, 仅特定功能
- 何时出现
- 偶尔随机, 人多的时候
- 负责方
- 主责 研发团队·服务器开发
- 研发团队要做的事
- 锁顺序规则;带超时的锁;看门狗,并留下停住瞬间的线程 dump。
- 监控图上
- 连接成批断开 · 连接数、服务器发送量
- 查看位置
- 停住期间抓取所有线程的调用栈。JVM 用 jstack(会自动查找并标出死锁),.NET 用 dotnet-stack,原生服务器用 gdb 的 thread apply all bt,或者先用 gcore 生成 core 文件,重启后再分析
- 确认依据
- 两个以上的线程停在互相等待对方所持锁的调用栈上,期间进程 CPU 使用率接近 0
- 排除依据
- 停住期间有一个线程以 100% CPU 在跑,是死循环。线程们都在等 DB 或外部响应,看同步调用或线程池耗尽
- 确认手段
- 运维工具即可确认(无需游戏代码)
出处
- Runtime locking correctness validator Linux kernel
以相反顺序获取两把锁,会因循环等待而死锁(lock inversion deadlock);Linux 内核会检查锁顺序并提前告警 - Liveness, Readiness, and Startup Probes Kubernetes
用存活探针(liveness probe)捕捉运行中却无法推进的死锁状态,重启容器 - Diagnostic Tools (Java SE 21 Troubleshooting Guide) Oracle
jstack 输出运行中 JVM 的所有线程堆栈,也会查找并标出死锁(Found one Java-level deadlock) - dotnet-stack diagnostic tool - .NET CLI Microsoft
抓取并输出 .NET 进程所有线程的托管堆栈 - Threads (Debugging with GDB) GNU Project
用 thread apply all 对所有线程执行同一条命令(bt:输出调用栈) - gcore(1) — Linux manual page gdb
为运行中的程序生成 core 文件,生成后程序照常继续运行
相关原因
同一层:L9 服务器游戏进程
其他层中同样导致“卡住”的原因
查看含图示和实验的原卡片