死锁排查
首先给出死锁定义:
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。
网上有很多讲死锁的文章,这里不做赘述,直接进入正题。
问题模拟代码
先写一段模拟代码,模拟死锁场景
我们创建两个锁和两个线程,让线程 1 先拥有锁 A,然后在 1s 后尝试获取锁 B,同时我们启动线程 2,让它先拥有锁 B,然后在 1s 之后尝试获取锁 A,这时就会出现相互等待对方释放锁的情况,从而造成死锁的问题,具体代码如下:
/** * <p> * 死锁 * </p> * @since 2022/3/27 12:58 */ @GetMapping("/deadLock") public void deadLock() { // 创建锁 A Object lockA = new Object(); // 创建锁 B Object lockB = new Object(); // 创建线程 1 Thread t1 = new Thread(() -> { // 先获取锁 A synchronized (lockA) { System.out.println("线程 1:获取到锁 A!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 尝试获取锁 B System.out.println("线程 1:等待获取 B..."); synchronized (lockB) { System.out.println("线程 1:获取到锁 B!"); } } }); t1.start(); // 运行线程 // 创建线程 2 Thread t2 = new Thread(() -> { // 先获取锁 B synchronized (lockB) { System.out.println("线程 2:获取到锁 B!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 尝试获取锁 A System.out.println("线程 2:等待获取 A..."); synchronized (lockA) { System.out.println("线程 2:获取到锁 A!"); } } }); t2.start(); // 运行线程 }
打包部署到服务器。并使用nohup java -jar &
运行。
排查过程
这里介绍2种排查方式:Jconsole 和 Arthas
Jconsole
部署之后请求一下接口: http://localhost:8080/deadLock
点击线程标签页,点击检测死锁。
直接就把检测死锁的线程告诉我们了。非常方便。
然后我们看线程的执行路径,定位到了94行和113行的代码。,接下来的事就好办了。
我们回到程序中看代码就知道这2个线程互相等待导致了死锁。
Arthas
Arthas是阿里的一款定位问题的工具,功能强大,最近我特别喜欢用这个。
官方的在线文档教程特别好玩,像是在玩游戏一样。
再告诉大家一个小技巧,IDEA插件安装 Alibaba Cloud Toolkit,可以直接在IDEA上进行问题排查,不用登录服务器。
首先我们安装下插件
安装完之后我们得先配置一下主机,输入服务器的账号密码
配置完成之后点击 诊断
它就会自动帮我们下载Arthas到服务器上,直接在IDEA实现异常诊断。
IDEA在手,天下我有
直接输入 thread -b
如果不知道命令用法可以先输入:命令 -h
查看帮助信息,或者直接看官方文档。
直接就帮我们找到了持有锁的线程,并且还展示了阻塞了多少个线程!
🐂B
然后我们就知道了是当前这个线程持有锁导致其他线程被阻塞,找到对应代码解决就OK了。
Arthas还有很多非常强大的功能,大家自行探索,还是非常有趣的,可以帮助我们更好的排查问题。
僵尸进程排查
僵尸(zombie)进程定义
僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源。
一般僵尸进程很难直接kill,不过可以先kill僵尸的父进程。父进程死后,僵尸进程成为“孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程,它产生的所有僵尸进程也跟着消失。
排查过程
首先使用 top
命令可以知道是否有僵尸进程
使用 ps -A -ostat,ppid,pid,cmd |grep -e |grep -e '^[Zz]'
命令定位僵尸进程
僵尸进程ID:25158
僵尸进程的父进程ID:22543
使用 kill -9 杀死僵尸进程的父进程
注:生产环境一定要检查确认僵尸进程的父进程退出是否对业务有影响