Windbg找出死锁
前言
本文介绍本人的一次使用Windbg分析dump文件找出死锁的过程,并重点介绍如何确定线程所等待的锁及判断是否出现了死锁。
对于如何安装及设置Windbg请参考:《使用Windbg和SoS扩展调试分析.NET程序》http://www.cnblogs.com/shanyou/archive/2006/12/23/601004.html
起因
今天,部署到生产环境中的软件再次发生了不响应请求的问题,看了系统日志与软件本身的log都没发现异常,而在任务管理器中软件占用了1G多的内存,有点偏高(正常是300M左右)。由于本人不在现场,只能通过远程的方式查看,同时故障出现间隔比较长(将近一周),在生产环境中也就无法使用VS进行调试。
无意中在资源监视器的CPU页看到软件的线程数是1.7万个,内存页的提交内存使用也将近18G,同时线程数与提交内存也在缓慢增加。当时就想是不是由于某种原因导致线程无法退出从而在线程数太多的时候致使软件不响应请求(后来的调试也证实是死锁导致的)。
由于故障难以重现(只在生产环境中长时间运行才会出现,在测试环境无法出现),只能对正在运行的软件进行分析。
这时候就请出了大名鼎鼎的Windbg,下面是详细的过程。
过程
一、抓取dump文件
抓取dump的方法,可以参考《抓取user mode dump文件的几重境界》http://www.cnblogs.com/pugang/archive/2013/02/18/2916211.html
我选择的是使用图形化操作的方式,在任务管理器的进程页中,右键需要抓取的程序,选择“创建转储文件”
运行完成后将会弹出成功对话框并提示dump文件的所在

二、在Windbg中加载dump文件与SOS.dll
运行Windbg,在File菜单下选择Open Crash Dump,选择上面抓取的dump文件
在Windbg下侧的命令输入框中输入“.load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos”并回车加载SOS.dll。由于我是调试net4.0 64位的软件,所以使用了Framework64\v4.0.30319下的sos,其它版本请选择对应位置的sos进行加载
三、使用Windbg查找死锁
使用“!threads”命令列出所有的线程,发现一共存在17306个线程
使用“~17306s”命令切换到最后一个线程,并使用“!clrstack”命令输出当前线程的调用堆栈,发现存在“System.Threading.Monitor.Enter(System.Object)”,表明线程正在请求一个锁。由于得不到锁,因此线程卡死
切换到其它线程查看调用堆栈,都是因为同样的原因导致线程卡死,这时候可以初步判断这些线程是因为死锁导致执行不下去
使用“!syncblk”命令列出所有正在使用的锁,其中MonitorHeld与Recursion列表示了请求锁的线程数量情况,Info列表示哪个线程拥有了锁,SyncBlock列表示锁对象的地址。如MonitorHeld与Recursion的值为3775与1那行表示第40个线程拥有了这个锁,其它(3775-1)/2=1887个线程在等待锁,锁对象地址为0000000003c812f0。看到如此多的线程在请求同一个锁,就知道情况不正常,看来离死锁的真相又近了一步
接下来的过程就是:找到某个线程(如线程A)请求的锁(如锁J),查看哪个线程(如线程B)拥有这个锁(锁J)及这个线程请求的锁(锁K),接着查看哪个线程(如线程C)拥有这个锁(锁K)及这个线程请求的锁(锁L),重复查看的过程,看最终是否有一个线程(如线程D)请求前面出现的任意一个锁(如线程B拥有的锁J),形成环状,这时即可判断其为死锁

这里从线程17306开始分析,使用“!clrstack -l”命令列出当前线程的调用堆栈及其使用的局部变量
在调用“System.Threading.Monitor.Enter(System.Object)”之前的一个方法内,应该存在作为局部变量的线程请求的锁对象
这里猜测下面的0000000003c812f0就是这个锁对象,通过查找上面的锁列表,确定了这个猜测,同时知道线程40拥有这个锁
使用“~40s”命令切换到线程40,并使用“!clrstack -l”命令列出当前线程的调用堆栈及其使用的局部变量,通过查找锁列表确定000000000317ac10为当前线程请求的锁对象,同时知道线程26拥有这个锁
同样使用“~26s”与“!clrstack -l”命令找到线程26请求的锁对象00000000044a81a8,这个锁对象被线程43拥有
接着使用“~43s”与“!clrstack -l”命令找到线程43请求的锁对象000000000317ac10,这个锁对象被线程26拥有
此时可以发现线程26与线程43之间形成了死锁

结果
终于真相大白了,上面的过程成功找到了死锁
也由此推断由于死锁的存在,导致后面新建的线程由于得不到请求的锁,一直不能执行下去,更不可能释放所占用的内存,从而使得线程数与内存占用在一直升高,直到软件无法响应请求为止
接下来的工作就是查看死锁线程的调用堆栈,结合软件源代码分析死锁形成时软件的运行情况,并更改处理逻辑以避免死锁的产生
Windbg找出死锁的更多相关文章
- 使用Windbg找出死锁,解决生产环境中运行的软件不响应请求的问题
前言 本文介绍本人的一次使用Windbg分析dump文件找出死锁的过程,并重点介绍如何确定线程所等待的锁及判断是否出现了死锁. 对于如何安装及设置Windbg请参考:<使用Windbg和SoS扩 ...
- Windbg找出memory leak的一种笨办法
以下内容是转自 http://www.cnblogs.com/fbird/p/5889596.html 以前做项目碰到过一个问题,在客户的站点上面发现有严重的内存泄漏.幸运的是我们找到了重现的步骤,一 ...
- 利用WinDbg找出程序崩溃的代码行号
之前碰到论坛里有几个好友,说程序不时的崩溃,什么xxoo不能read的! 如果光要是这个内存地址,估计你会疯掉~~ 所以分享一下基本的调试技巧,需要准备的工具有WinDbg + VC6.0, 下面是自 ...
- 用windbg+sos找出程序中谁占用内存过高,谁占用CPU过高(转载)
原文地址: http://www.cnblogs.com/Lawson/archive/2011/01/23/1942692.html 很早看到windbg+sos方面的知识,一直没仔细学习,也许因为 ...
- SQL Server如何找出一个表包含的页信息(Page)
在SQL Server中,如何找到一张表或某个索引拥有那些页面(page)呢? 有时候,我们在分析和研究(例如,死锁分析)的时候还真有这样的需求,那么如何做呢? SQL Server 2012提供了一 ...
- Entity Framework 6 Recipes 2nd Edition(9-3)译->找出Web API中发生了什么变化
9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Fri ...
- 使用T-SQL找出执行时间过长的作业
有些时候,有些作业遇到问题执行时间过长,因此我写了一个脚本可以根据历史记录,找出执行时间过长的作业,在监控中就可以及时发现这些作业并尽早解决,代码如下: SELECT sj.name , ...
- [LeetCode] Find All Numbers Disappeared in an Array 找出数组中所有消失的数字
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and ot ...
- [LeetCode] Find All Duplicates in an Array 找出数组中所有重复项
Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others ...
随机推荐
- GPS转换为百度坐标
原文地址:http://www.cnblogs.com/zhaohuionly/archive/2013/06/18/3142623.html 最近在做一个关于手机定位的小应用,需求是这样的,用户通过 ...
- JS 精度问题处理
/除法函数,用来得到精确的除法结果 //说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显.这个函数返回较为精确的除法结果. //调用:accDiv(arg1,arg2) ...
- strip 使用命令
使用 通过消除使用调试器的粘合剂和符号信息,减少扩展公共对象文件格式(XCOFF)对象文件大小. 语法 strip [ -V ] [ -r [ -l ] | -x [ -l ] | -t | -H | ...
- 【从翻译mos文章】oracle linux 和外部存储系统 关系
oracle linux 和外部存储系统 关系 参考原始: Oracle Linux and External Storage Systems (Doc ID 753050.1) 范围: Linux ...
- C# Parse和Convert的区别分析(转)
大家都知道在进行类型转换的时候有连个方法供我们使用就是Convert.to和*.Parse,但是疑问就是什么时候用C 什么时候用P 通俗的解释大家都知道: Convert 用来转换继承自object类 ...
- C++中避免内存泄露常见的解决方式
常见内存泄露及解决方式-选自ood启发录 new/delete, array new/arrray delete匹配 case 1: 在类的构造函数与析构函数中没有匹配地调用 new/delete! ...
- hdu 4869 Task(馋)
题目链接:hdu 4869 Task 题目大意:有n台机器,m个任务.每一个机器和任务都有有xi和yi.要求机器的xi.yi均大于等于任务的xi和yi才干运行任务. 每台机器一天仅仅能运行一个任务.要 ...
- Java笔试题集锦
Java笔试题集锦 1.MVC的各个部分都有那些技术来实现?怎样实现? 答:MVC是Model-View-Controller的简写."Model" 代表的是应用的业务逻辑(通过J ...
- hibernate的orphanRemoval
在@OneToMany与@OneToOne中使用orphanRemoval = true时候 改动保存时候setXXX org.springframework.orm.hibernate3.Hiber ...
- Spring 之 第一个hellword
Rod Johnson在2002年编著的<Expert one to one J2EE design and development>一 书中,对Java EE正统框架臃肿.低效.脱离现实 ...