您的位置:首页 > 其它

WinDbg初始体验2,抓住不放的恶棍(OutOfMemory案例分析Ⅰ)

2007-11-24 11:27 459 查看
上篇中,介绍了如何使用adplus来捕捉dump。我们可以使用这个命令:adplus -Crash -p 进程ID(或-IIS) -quiet -fullonfirst -o C:\dumps 。但是我们在哪里执行这段命令呢?在控制台命令行,切换到Windbg的安装目录下。然后执行上面的命令,当你监控的进程出现异常时,它就会捕捉到该进程的所有相关信息到dump文件中(前提是fullonfirst)。比如你的进程使用内存是1G,那么dump文件也会在1G左右。此时如果你想捕捉OutOfMemory的dump,不一定要等到这个异常出现后。你可以在内存到一定量的时候,去获取dump文件,基本的信息是一样的。

我捕捉到的dump文件是1.31 GB ,太大了,过了这个值,网站马上就Crash掉了。可能是由于服务器物理架构的原因,这个dump文件在我本机打不开,只有在服务器上调试了。打开dump文件,先来一段看不懂的提示:

This dump file has an exception of interest stored in it.

The stored exception information can be accessed via .ecxr.

(39c.13c): CLR exception - code e0434f4d (first/second chance not available)

eax=0275f290 ebx=000d6848 ecx=00000000 edx=00000024 esi=0275f31c edi=e0434f4d

eip=7c80bee7 esp=0275f28c ebp=0275f2e0 iopl=0 nv up ei pl nz na po nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202

*** ERROR: Symbol file could not be found. Defaulted to export symbols for kernel32.dll -

kernel32!RaiseException+0x3c:

7c80bee7 5e pop esi

大概意思就是说:dump文件中有一个异常,各个寄存器的地址(应该是吧),找不到kernel32.dll的Symbol ,异常是由它引发,略略。

这时,我们在工作窗体中执行命令加载SOS.dll。.load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\SOS.dll

接下来我们就可以开始工作了,先执行命令:!eeheap -gc 看看托管堆上都放着些什么。

0:012> !eeheap -gc

PDB symbol for mscorwks.dll not loaded

Number of GC Heaps: 4

------------------------------

Heap 0 (000e28e0)

generation 0 starts at 0x6d110044

generation 1 starts at 0x6d110038

generation 2 starts at 0x02860038

ephemeral segment allocation context: none

segment begin allocated size

1bd8c710 7b45382c 7b46a268 0x00016a3c(92732)

000f6b40 7a72c42c 7a74d308 0x00020edc(134876)

000eafb0 790d5588 790f4b38 0x0001f5b0(128432)

02860000 02860038 0685ea18 0x03ffe9e0(67103200)

2cca0000 2cca0038 30c9db38 0x03ffdb00(67099392)

44f30000 44f30038 48d01460 0x03dd1428(64820264)

6d110000 6d110038 6d112050 0x00002018(8216)

Large object heap starts at 0x12860038

segment begin allocated size

12860000 12860038 14844b50 0x01fe4b18(33442584)

35c80000 35c80038 37b42920 0x01ec28e8(32254184)

Heap Size 0xfccdbe8(265083880)

------------------------------

Heap 1 (000e39b0)

generation 0 starts at 0x4ff2e1d4

generation 1 starts at 0x4ff2b4fc

generation 2 starts at 0x06860038

ephemeral segment allocation context: none

segment begin allocated size

06860000 06860038 0a85db18 0x03ffdae0(67099360)

20aa0000 20aa0038 24a9ccb0 0x03ffcc78(67095672)

37d90000 37d90038 3bd8d500 0x03ffd4c8(67097800)

4efc0000 4efc0038 4ff321e0 0x00f721a8(16196008)

Large object heap starts at 0x14860038

segment begin allocated size

14860000 14860038 16846380 0x01fe6348(33448776)

30d60000 30d60038 32d0e808 0x01fae7d0(33220560)

52fc0000 52fc0038 535b4598 0x005f4560(6243680)

Heap Size 0x114f2e40(290401856)

------------------------------

Heap 2 (000e4e70)

generation 0 starts at 0x447270a0

generation 1 starts at 0x447268f8

generation 2 starts at 0x0a860038

ephemeral segment allocation context: none

segment begin allocated size

0a860000 0a860038 0e85f1e4 0x03fff1ac(67105196)

24ca0000 24ca0038 28c9f548 0x03fff510(67106064)

40730000 40730038 4472b0ac 0x03ffb074(67088500)

Large object heap starts at 0x16860038

segment begin allocated size

16860000 16860038 188296c8 0x01fc9690(33330832)

1e890000 1e890038 20849ab0 0x01fb9a78(33266296)

4cfc0000 4cfc0038 4ef6aef0 0x01faaeb8(33205944)

Heap Size 0x11f276f0(301102832)

------------------------------

Heap 3 (000e63a8)

generation 0 starts at 0x40694068

generation 1 starts at 0x4069405c

generation 2 starts at 0x0e860038

ephemeral segment allocation context: none

segment begin allocated size

0e860000 0e860038 1285df98 0x03ffdf60(67100512)

28ca0000 28ca0038 2cc9ef68 0x03ffef30(67104560)

3c730000 3c730038 4069a074 0x03f6a03c(66494524)

Large object heap starts at 0x18860038

segment begin allocated size

18860000 18860038 1a85c5e0 0x01ffc5a8(33539496)

33c80000 33c80038 35c3b740 0x01fbb708(33273608)

56bb0000 56bb0038 57133f60 0x00583f28(5783336)

Heap Size 0x104a2aa4(273296036)

------------------------------

GC Heap Size 0x4358abbc(1129884604)

首先提示我们一下,存放mscorwks.dll symbol 的PDB文件不存在。然后告诉我们有4个GC拖管堆的存在。每个拖管堆都详细罗列出了每一个代龄堆的起始地址,以及每个堆上面存放的对象所占用内存空间。最后给我们统计了一下GC堆的大小,共有1129884604 bytes (1.1G左右),也就是说,当进程的内存大部分都被拖管堆给占用了。大对象有不少呢!接下来的命令是!dumpheap -min 85000 -stat 统计一下大对象的基本信息。

0:012> !dumpheap -min 85000 -stat

------------------------------

Heap 0

total 213 objects

------------------------------

Heap 1

total 207 objects

------------------------------

Heap 2

total 296 objects

------------------------------

Heap 3

total 230 objects

------------------------------

total 946 objects

Statistics:

MT Count TotalSize Class Name

7912254c 6 746928 System.Object[]

7912677c 13 5889000 System.Single[]

79122610 3 9465840 System.Collections.Hashtable+bucket[]

000d88f0 105 36952120 Free

7912273c 335 37943440 System.Byte[]

79122414 484 219252000 System.Int32[]

Total 946 objects

好家伙,有946个大对象。而且单单整型数组就占用了200多M的内存,是什么东西在搞鬼呢?看看下面命令,这会是列出所有的大对象,不是得到统计信息:!dumpheap -min 85000

Heap 0

Address MT Size

128823a0 7912273c 113264

128a2818 7912273c 113264

128be298 79122414 453000

1292cc20 7912273c 113264

12948690 000d88f0 107448 Free

12962c68 7912254c 124488

129812b0 000d88f0 107296 Free

1299b5d0 79122414 453000

.......

35ae0ed8 000d88f0 286800 Free

35b26f28 7912273c 113264

35b42998 7912677c 453000

35bb1338 79122414 453000

35c1fcd0 7912273c 113264

56bb0038 79122414 453000

56c1e9c0 7912273c 113264

56c3a430 000d88f0 339776 Free

56c8d370 79122414 453000

56cfbd08 7912273c 113264

56d17788 79122414 453000

56d86110 79122414 453000

total 230 objects

------------------------------

total 946 objects

Statistics:

MT Count TotalSize Class Name

7912254c 6 746928 System.Object[]

7912677c 13 5889000 System.Single[]

79122610 3 9465840 System.Collections.Hashtable+bucket[]

000d88f0 105 36952120 Free

7912273c 335 37943440 System.Byte[]

79122414 484 219252000 System.Int32[]

看出规律了吧?从Heap0到Heap3,都是存在这样的两种大小的对象:113264 和 453000 。有规律就好办事了,找一个出来看看,执行命令:!gcroot 0x34e9bee0

0:012> !gcroot 0x34e9bee0

Note: Roots found on stacks may be false positives. Run "!help gcroot" for

more info.

Scan Thread 12 OSTHread 13c

Scan Thread 18 OSTHread 1580

Scan Thread 19 OSTHread 1510

.....扫描太多线程了

DOMAIN(000FF798):HANDLE(Pinned):1aec11ec:Root:12870d18(System.Object[])->

070db534(Lucene.Net.Search.FieldSortedHitQueue+AnonymousClassCache)->

070db540(System.Collections.Hashtable)->

0634bfe4(System.Collections.Hashtable+bucket[])->

3e886dd4(System.Collections.Hashtable)->

3e886e0c(System.Collections.Hashtable+bucket[])->

3e887164(Lucene.Net.Search.FieldSortedHitQueue+AnonymousClassScoreDocComparator2)->

3e887154(Lucene.Net.Search.StringIndex)->

34e9bee0(System.Int32[])

看看,这都是什么东西啊?在程序中使用了Lucene.net,赶紧打开Reflector。查了查,看了看。我看到了缓存对象的实现,看到了每搜索一次都往缓存里加对象。但是我却没有看到缓存的过期和回收。怎么办呢?没有开关,没有配置。我只有通过源码找到Cache的实现,将它的public virtual object Get(IndexReader reader, object key)方法重写了,我不缓存了,不行吗!然后向Lucene.net报告这个情况。(也不知道这该不该认为是一个BUG呢,顺便说一下,Lucene.net已经有 2.1.0.2版本了,只不过是beta,还没有正式公布出来,我是在SVN服务器上看到的)。

缓存,是大多数内存问题的根源所在。有很多简单的缓存实现都是用一个Hashtable来缓存对象,但是它们却始终都没有做缓存过期的回收工作,甚至连过期策略都没有。这由不得让我想起了Enterprise Library的缓存,它有相当完善的缓存过期管理。但由于entlib使用和配置的问题,现在已基本不用了。现用的缓存基本也都使用HttpRumtime.Cache的缓存,虽然听很多人说使用它会有很多问题,比如自动释放。但是使用它也有很多好处呀,比如在web.config我们可以配置它的缓存策略,配置它要不定期回收,配置它可以使用的内存比例。总之,在一些场合,我们还是可以使用的。前提是,它适合我们,不会给你的程序带来很多的负面影响。使用Hashtable来缓存也是一样的,只要缓存项是有限的,那就可以用。

总算是找到了一个成功的找到了一个问题,兴奋之余接下来就是升级,看看效果了。但是,这个故事结束之后,看到的服务器的内存利用率还是暴涨,似乎没有任何改观?难道是我们的修改没有正确?预知详情,还请继续关注下一篇:《WinDbg初始体验3,剪不断的链条

下一篇引用:WinDbg初始体验3,剪不断的链条

上一篇引用:WinDbg初始体验,问题来了怎么办?

阿不
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: