日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

分析性能瓶颈 — 调试OutOfMemoryException

發(fā)布時(shí)間:2025/3/17 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分析性能瓶颈 — 调试OutOfMemoryException 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在前面的文章里面,執(zhí)行性能測(cè)試—起步里,講了執(zhí)行性能測(cè)試的基本步驟,而且在前面的例子里面,通過(guò)一個(gè)2M多的文本文件,對(duì)比了冒泡排序和快速排序的性能之間的差別。但是當(dāng)我使用一個(gè)700M大小的文本文件進(jìn)行測(cè)試的時(shí)候—因?yàn)槲倚枰私庠跇O端情況下兩種排序方法的差別。原定是2G的文本文件,但是無(wú)論快排還是冒泡排序都要求被排序的數(shù)據(jù)完全存在于內(nèi)存當(dāng)中,對(duì)于32位機(jī),2G的數(shù)據(jù)是一個(gè)上限,因?yàn)椴僮飨到y(tǒng)的內(nèi)核代碼使用掉了另外2G的地址空間—除非你使用/LARGEADDRESSAWARE這個(gè)開(kāi)關(guān),限制操作系統(tǒng)只使用1G的內(nèi)存,而讓用戶態(tài)代碼使用3G的空間。

?

為了重現(xiàn)這個(gè)問(wèn)題,我們先來(lái)準(zhǔn)備一下數(shù)據(jù),用下面兩個(gè)DOS命令就可以準(zhǔn)備好這些數(shù)據(jù)了:

?

1.?? dir /s /b c:\windows > d:\test.txt

2.?? FOR /L %i IN (1,1,100) DO type test.txt >> testdata.txt

?

第一個(gè)命令將Windows文件夾里面所有子文件夾的文件列表都輸出到test.txt文件里,第二個(gè)命令循環(huán)100遍,將test.txt文件內(nèi)容追加到testdata.txt里面,這樣就可以生成好幾百兆大小的文本文件了。

?

數(shù)據(jù)生成好了以后,使用執(zhí)行性能測(cè)試—起步貼出來(lái)的程序執(zhí)行一遍,就可以看到OutOfMemeoryException了(如果你沒(méi)有看到這個(gè)異常,可能是你的機(jī)器太強(qiáng)大了,請(qǐng)換一個(gè)更大的文件)。在調(diào)試器里面執(zhí)行后出現(xiàn)異常后,加載進(jìn)SOS進(jìn)行分析:

?

?(318.1688): CLR exception - code e0434352 (first chance)

… …

#

# OutOfMemeoryException已經(jīng)拋出了

#

?(318.1688): CLR exception - code e0434352 (!!! second chance !!!)

eax=0017eb74 ebx=00000005 ecx=00000005 edx=00000000 esi=0017ec20 edi=005595e0

eip=753b9617 esp=0017eb74 ebp=0017ebc4 iopl=0???????? nv up ei pl nz ac pe nc

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

KERNELBASE!RaiseException+0x54:

753b9617 c9????????????? leave

0:000> .loadby sos clr

0:000> !pe

Exception object: 744a8e84

Exception type:?? System.OutOfMemoryException

Message:????????? <none>

InnerException:?? <none>

StackTrace (generated):

SP?????? IP?????? Function

#

# 不出所料,StreamReader里面使用StringBuilder將文本文件讀入到一個(gè)字符串里。而StringBuilder是動(dòng)態(tài)

# 分配內(nèi)存的。

#

??? 0017ED38 5713E61E mscorlib_ni!System.Text.StringBuilder.ToString()+0x1e

??? 0017ED64 57121991 mscorlib_ni!System.IO.StreamReader.ReadToEnd()+0x7d

??? 0017ED78 002F0136 ConsoleApplication1!ConsoleApplication1.Program.Main(System.String[])+0xc6

?

StackTraceString: <none>

HResult: 8007000e

#

# 使用AnalyzeOOM來(lái)分析一下原因,看看是GC哪一個(gè)內(nèi)存區(qū)域?qū)е铝诉@個(gè)異常。

#

0:000> !ao

#

# 找到了,是大對(duì)象堆(Large Object Heap - LOH),GC在嘗試申請(qǐng)一個(gè)1.5G的內(nèi)存空間時(shí)

# 遭拒。

#

Managed OOM occured after GC #312 (Requested to allocate 1649667816 bytes)

Reason: Didn't have enough memory to allocate an LOH segment

Detail: LOH: Failed to reserve memory (1652555776 bytes)

#

# 使用!eeheap –gc命令找到GC里,各個(gè)generation的堆的起始地址和結(jié)束地址

#

0:000> !eeheap -gc

Number of GC Heaps: 1

generation 0 starts at 0x744a8e78

generation 1 starts at 0x7446a408

generation 2 starts at 0x01911000

ephemeral segment allocation context: none

?segment???? begin allocated?size

01910000?01911000?0290aee0?0xff9ee0(16752352)

03fd0000?03fd1000?04fcd3ec?0xffc3ec(16761836)

… …

73ca0000?73ca1000?744aae84?0x809e84(8429188)

Large object heap starts at 0x02911000

#

# LOH的起始地址找到了,0x02911000就是LOH的起始地址

# 已經(jīng)分配了0x02913240個(gè)字節(jié),所以結(jié)束地址就是

# 0x02911000 + 0x02913240

#

?segment???? begin allocated?size

02910000?02911000?02913240?0x2240(8768)

Total Size:????????????? Size: 0x629424c0 (1653875904) bytes.

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

GC Heap Size:??????????? Size: 0x629424c0 (1653875904) bytes.

#

# 使用dumpheap看一下LOH當(dāng)中各個(gè)對(duì)象的內(nèi)存分配情況。

#

# dumpheap的參數(shù)中,0x02911000是要查看的內(nèi)存的起始地址,

# 0x02911000 + 0x02913240是查看的結(jié)束地址

#

0:000> !dumpheap -stat 02911000?02911000+02913240

total 0 objects

Statistics:

????? MT??? Count??? TotalSize Class Name

00529748??????? 4?????????? 84????? Free

57166c28??????? 3???????? 8720 System.Object[]

#

# 不出所料,StringBuilderChar[]對(duì)象最多,但是

# Char[]的數(shù)組大小有171M之多。

#

571afb78???? 1119??????? 31332 System.Text.StringBuilder

571b1d88???? 1120???? 17933440 System.Char[]

Total 2246 objects

#

# 使用!dumpheap-mt參數(shù)看看char[]數(shù)組的詳細(xì)內(nèi)存分配情況

#

# 571b1d88這個(gè)參數(shù),來(lái)自于前面的!dumpheap命令的輸出(注意高亮顯示的地方)

# 這個(gè)參數(shù)告訴dumpheap命令,只檢索char[]數(shù)組(Method table571b1d88

# 的情況。

#

0:000> !dumpheap -mt 571b1d88 02911000 02911000+02913240

?Address?????? MT???? Size

03fd1000 571b1d88??? 16012????

… …

05222c90 571b1d88??? 16012????

total 0 objects

Statistics:

????? MT??? Count??? TotalSize Class Name

571b1d88???? 1120???? 17933440 System.Char[]

Total 1120 objects

#

# 隨便選一個(gè)對(duì)象(注意上面一個(gè)高亮顯示的文本),看看它到底被誰(shuí)引用了,

# 導(dǎo)致GC一直不釋放它,畢竟我電腦的有2G的物理內(nèi)存,讀取幾百兆的文件,

# 就觸發(fā)了OutOfMemoryException,的確有點(diǎn)離譜。

#

0:000> !gcroot 03fd1000

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

more info.

Scan Thread 0 OSTHread 1688

ESP:17eca4:Root:?0191d2e0(System.Text.StringBuilder)->

?744a4fd0(System.Text.StringBuilder)->

?… …

?5de44814(System.Text.StringBuilder)->

Command cancelled at the user's request.

?03fd1000(System.Char[])

#

# 再看看0x0191d2e0這個(gè)StringBuilder對(duì)象都在哪些地方被引用到了,

# 根據(jù)GC的實(shí)現(xiàn),堆棧上各個(gè)函數(shù)的局部變量是當(dāng)作root來(lái)處理的

#

0:000> !gcroot 0191d2e0

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

more info.

Scan Thread 0 OSTHread 1688

ESP:17eca4:Root:?0191d2e0(System.Text.StringBuilder)

ESP:17eca8:Root:?0191d2e0(System.Text.StringBuilder)

ESP:17ecb4:Root:?0191d2e0(System.Text.StringBuilder)

ESP:17ecbc:Root:?0191d2e0(System.Text.StringBuilder)

ESP:17ed3c:Root:?0191d2e0(System.Text.StringBuilder)

ESP:17ed58:Root:?0191d2e0(System.Text.StringBuilder)

Scan Thread 2 OSTHread ce8

#

# 從前面的輸出,隨便找兩個(gè)對(duì)象(前面標(biāo)注的文本),看看它們都是哪些函數(shù)的局部變量

# 例如17ed58這個(gè)地址介于System.IO.StreamReader.ReadToEnd()

# System.Text.StringBuilder.ToString()之間,也就是StreamReader.ReadToEnd()

# 這個(gè)函數(shù)定義的,至此,基本上我們可以認(rèn)為已經(jīng)找到影響性能的元兇了。

#

0:000> !dumpstack

OS Thread Id: 0x1688 (0)

Current frame: KERNELBASE!RaiseException+0x54

ChildEBP RetAddr?Caller, Callee

0017eb7c 753b9617 KERNELBASE!RaiseException+0x54, calling ntdll!RtlRaiseException

0017eba0 5888bc5e clr!DllUnregisterServerInternal+0x15c62, calling clr!DllUnregisterServerInternal+0xa55b

0017ebac 588fa99c clr!LogHelp_TerminateOnAssert+0x2df44, calling clr!DllUnregisterServerInternal+0x15c45

0017ebbc 58871bd0 clr+0x1bd0, calling clr+0x1b8b

0017ebc4 588fac08 clr!LogHelp_TerminateOnAssert+0x2e1b0, calling KERNEL32!RaiseException

0017ec54 5896ab0b clr!CopyPDBs+0x4abd, calling clr!LogHelp_TerminateOnAssert+0x2e03e

0017ec90 58b27c9e clr!CorLaunchApplication+0x11756, calling clr!CopyPDBs+0x4a78

0017ecdc 588908f6 clr!CoUninitializeEE+0x272e, calling clr!GetMetaDataInternalInterface+0xde18

0017ed30 5713e61e (MethodDesc 56f0c09c +0x1e System.Text.StringBuilder.ToString()), calling 00242350

0017ed5c 57121991 (MethodDesc 56efff40 +0x7d System.IO.StreamReader.ReadToEnd())

0017ed70 002f0136 (MethodDesc 00253840 +0xc6 ConsoleApplication1.Program.Main(System.String[]))

0017edd4 588721db clr+0x21db

0017ede4 58894a2a clr!CoUninitializeEE+0x6862, calling clr+0x21a8

… …

0017f91c 589daf00 clr!CorExeMain+0x1c, calling clr!GetCLRFunction+0xd6a

0017f954 718455ab mscoreei!CorExeMain+0x38

0017f960 6f667f16 MSCOREE!CreateConfigStream+0x13f

0017f970 6f664de3 MSCOREE!CorExeMain+0x8, calling MSCOREE!CorExeMain+0x2f14

0017f978 75d41194 KERNEL32!BaseThreadInitThunk+0x12

0017f984 7723b495 ntdll!RtlInitializeExceptionChain+0x63

0017f9c4 7723b468 ntdll!RtlInitializeExceptionChain+0x36, calling ntdll!RtlInitializeExceptionChain+0x3c

總結(jié)

以上是生活随笔為你收集整理的分析性能瓶颈 — 调试OutOfMemoryException的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。