Windbg程序调试是.NET高级开发需要掌握的必备技能,分析内存泄露、分析CPU高使用率、分析线程阻塞、分析内存对象、分析线程堆栈、Live Dedugging。这些Debug领域可以说一个技能+场景化应用的结合,如果只依靠Windbg几个简单的命令,不理解每个指令在实际Troubleshooting中的作用,是没有意义的。工欲善其事必先利其器,我们先从常用的命令和示例说起。
Windbg 常用命令
先准备一个Dump文件,建议使用64位应用程序。例如:64位IIS应用的w3wp进程,64位exe进程都可以。如果抓Dump文件,很简单:任务管理器-进程-右键【创建转储文件】。
然后下载并安装Windbg,下载链接:https://developer.microsoft.c...,一路下一步,选择【Debugging Tools for Windows】。
打开之后,Ctrl+D,打开第一步抓的Dump文件,开始今天的常用命令介绍。
- 加载SOS调试扩展dll。
.loadby sos clr
- 设置并重新加载调试符号文件的命令,将.Net 一些重要的pdb文件下载到指定的路径中,加载到Windbg调试环境中,这样,我们就可以看到程序在哪一行出错,运行到哪一行了。
.symfix+ C:\symbols
.reload
- 打印当前调试符号文件搜索路径。
0:000> .sympath
- 查看线程池,分析并确认CPU使用率,可以使用哪个指令。
0:000> !threadpool
CPU utilization: 2%
Worker Thread: Total: 19 Running: 2 Idle: 17 MaxLimit: 32767 MinLimit: 4
Work Request in Queue: 0
--------------------------------------
Number of Timers: 2
--------------------------------------
Completion Port Thread:Total: 4 Free: 4 MaxFree: 8 CurrentLimit: 4 MaxLimit: 1000 MinLimit: 4
- 查看线程的整体运行情况。
!threads
- 查询指定线程的调用堆栈,例如34号线程。
~34s
!clrstack
- 查看线程消耗CPU资源情况。
!runaway
第一列是线程号,第二列是Total的CPU使用时间
- 查看当前线程栈上所有对象的信息,Dump stack objects。
- 查询内存中指定对象的信息 Dump object。
!do
- 查询内存中指定数组对象的信息 Dump Array。
!de
- 查看当前线程的堆栈和每行堆栈上的变量信息。
!clrstack -a
- Windbg 附加进程调试,启用CLR异常捕获、查看异常、查看异常所在线程堆栈、禁用CLR异常调试、退出调试。
sxe clr
g
!pe
!clrstack
sxd clr
qd
- 查看托管堆上内存对象的分布、三个代的信息。
!eeheap -gc
- 查看托管堆上加载的Dll。
!eeheap -loader
- 查询内存中各类对象的总个数和总内存占用。
!dumpheap -stat
- 查询内存中大对象的个数和对象大小。
!dumpheap -stat -mt -min 85000
- 查看内存的析构队列的指令。
!finalizequeue
- 请输入查看对象000000123557DFC0的gcroot的指令。
!gcroot 000000123557DFC0
- 查看线程阻塞的指令。
!syncblk
- 查看Dump中所有System.Net.Sockets.Socket对象统计信息的指令。
!dumpheap -type System.Net.Sockets.Socket -stat
以上是Windbg常用的命令和使用说明,接下来和大家分享使用Windbg分析.NET应用内存泄露问题。
使用Windbg分析.NET应用内存泄露问题
我们按以下的思路展开今天的分享:
- 描述问题背景和现象
- 确定问题是否是内存泄露
- 整理问题分析思路
- 动手分析解决
- 总结复盘
先说问题背景
生产环境IIS站点,运行一段时间后,w3wp进程内存会涨到2G,同时内存不释放。
问题确认
打开性能计数器,我们重点看一段时间内,IIS站点w3wp进程相关的性能计数器的变化:
性能计数器中:有三个非常重要:
- .NET CLR Memory/Gen 2 heap size
- .NET CLR Memory/Gen 1 heap size
- .NET CLR Memory/Gen 0 heap size
托管堆上的对象有三代:
第 0 代:
这是最年轻的代,其中包含短生存期对象。短生存期对象的一个示例是临时变量。垃圾回收最常发生在此代中。新分配的对象构成新一代的对象并且为隐式的第 0 代回收,除非它们是大对象,在这种情况下,它们将进入第 2 代回收中的大对象堆。大多数对象通过第 0 代中的垃圾回收进行回收,不会保留到下一代。
第 1 代:
这一代包含短生存期对象并用作短生存期对象和长生存期对象之间的缓冲区。
第 2 代:
这一代包含长生存期对象。长生存期对象的一个示例是服务器应用程序中的一个包含在进程期间处于活动状态的静态数据的对象。
当条件得到满足时,垃圾回收将在特定代上发生。回收某个代意味着回收此代中的对象及其所有更年轻的代。第 2 代垃圾回收也称为完整垃圾回收FullGC,因为它回收所有代上的所有对象(即,托管堆中的所有对象)。
幸存和提升:
垃圾回收中未回收的对象也称为幸存者,并会被提升到下一代。在第 0 代垃圾回收中幸存的对象将被提升到第 1 代;在第 1 代垃圾回收中幸存的对象将被提升到第 2 代;而在第 2 代垃圾回收中幸存的对象将仍为第 2 代。
通过代提升,看对象的存活时间!
Process/Private Bytes
Process/Virtual Bytes
.NET CLR Memory/# Bytes in all Heaps : CLR内存托管堆的大小
.NET CLR Memory/Large Object Heap Size: 大对象堆包含其大小为 85,000 个字节和更多字节的对象。
托管堆的内存大小增加的趋势和大对象堆增加的趋势重叠,可以初步推断,内存的增加和大对象有关系!
整理问题分析思路
连续、间隔抓两个或者三个Dump,每次抓Dump间隔半个小时,或者一个小时,通过多个Dump对比着看内存的增量。
对比的看每个Dump中:
- 多核CPU情况下,分析每个GC托管堆的大小 !eeheap –gc
- 查询内存中各类对象的总个数和总内存占用 !dumpheap –stat
- 查询内存中大对象的个数和对象大小 !dumpheap –stat -mt -min 85000
- 如果某一类或者几类对象的内存总占用很多,分析此类对象 !dumpheap –mt *
- 多次采样查看步骤4中对象的gcroot !gcroot addr
打断gcroot中任何一个链条,释放对象引用
动手分析解决
多核CPU情况下,分析每个GC托管堆的大小 !eeheap –gc。
查询内存中各类对象的总个数和总内存占用 !dumpheap –stat。
查询内存中大对象的个数和对象大小 !dumpheap –stat -mt -min 85000。
如果某一类或者几类对象的内存总占用很多,分析此类对象 !dumpheap –mt * -stat。
大对象字符串分析,可以上图结合具体的代码,可以发现是用户Session会话数据!同时Session会话中包含了整个用户的权限数据!
多次采样查看步骤4中对象的gcroot !gcroot addr。
打断gcroot中任何一个对象的连接引用,完成对象引用的释放。
总结
分析解决.NET应用程序的内存泄露问题,大致的套路是这样的:
- 描述问题背景和现象
- 确定问题是否是内存泄露
- 梳理问题分析思路
- 动手分析解决
- 总结复盘
详细的分析步骤如下:
- 多核CPU情况下,分析每个GC托管堆的大小 !eeheap –gc
- 查询内存中各类对象的总个数和总内存占用 !dumpheap –stat
- 查询内存中大对象的个数和对象大小 !dumpheap –stat -mt -min 85000
- 如果某一类或者几类对象的内存总占用很多,分析此类对象 !dumpheap –mt *
- 多次采样查看步骤4中对象的gcroot !gcroot addr
- 打断gcroot中任何一个引用链条,释放对象引用
.NET应用程序的内存泄露问题分析,有固定的方法和套路,整个分析过程需要大家深入理解,同时对代码的熟悉非常重要,以上分享希望能对大家的日常线上问题分析有所帮助。
微软最有价值专家(MVP)
微软最有价值专家是微软公司授予第三方技术专业人士的一个全球奖项。28年来,世界各地的技术社区领导者,因其在线上和线下的技术社区中分享专业知识和经验而获得此奖项。
MVP是经过严格挑选的专家团队,他们代表着技术最精湛且最具智慧的人,是对社区投入极大的热情并乐于助人的专家。MVP致力于通过演讲、论坛问答、创建网站、撰写博客、分享视频、开源项目、组织会议等方式来帮助他人,并最大程度地帮助微软技术社区用户使用Microsoft技术。
更多详情请登录官方网站:
https://mvp.microsoft.com/zh-cn
欢迎关注微软中国MSDN订阅号,获取更多最新发布!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。