.NET?CPU爆高事故事故分析某供應(yīng)鏈WEB網(wǎng)站
一:背景
1. 講故事
年前有位朋友加微信求助,說(shuō)他的程序出現(xiàn)了偶發(fā)性CPU爆高,尋求如何解決,截圖如下:
我建議朋友用 procdump 在 cpu 高的時(shí)候連抓兩個(gè)dump,這樣分析起來(lái)比較穩(wěn)健,朋友也如期的成功抓到,接下來(lái)就用 windbg 一起來(lái)分析下吧。
二:Windbg 分析
1. 查看CPU占用率
先用 !tp 查看兩個(gè) dump 的cpu 利用率
0:112> !tp CPU utilization: 100% Worker Thread: Total: 138 Running: 128 Idle: 10 MaxLimit: 2000 MinLimit: 400 Work Request in Queue: 17 Unknown Function: 00007ffe1a6617d0 Context: 000001fd9bcb20c8 ... -------------------------------------- Number of Timers: 2 -------------------------------------- Completion Port Thread:Total: 2 Free: 2 MaxFree: 16 CurrentLimit: 2 MaxLimit: 2000 MinLimit: 400 0:014> !tp CPU utilization: 96% Worker Thread: Total: 173 Running: 67 Idle: 106 MaxLimit: 2000 MinLimit: 400 Work Request in Queue: 1 Unknown Function: 00007ffe1a6617d0 Context: 000001fda1a20be8 -------------------------------------- Number of Timers: 2 -------------------------------------- Completion Port Thread:Total: 7 Free: 7 MaxFree: 16 CurrentLimit: 7 MaxLimit: 2000 MinLimit: 400
果然如朋友所述,接下來(lái)就可以試探的看下是不是 GC 觸發(fā)導(dǎo)致 ?
2. 查看是否 GC 觸發(fā)
干脆一點(diǎn)就是用 ~*e !dumpstack 導(dǎo)出所有線程的托管和非托管棧,然后搜索 GarbageCollectGeneration 就好了。
果然是觸發(fā)了 GC,從調(diào)用棧信息看,當(dāng)前托管層可能正在高頻的 new 操作,導(dǎo)致只往某一個(gè)heap上狂寫(xiě)數(shù)據(jù)從而致 heap 失衡,服務(wù)器模式GC為了讓多 heap 均衡,做了 heap balance 操作,接下來(lái)的線索是為什么有狂寫(xiě)的情況? 還得看下托管層,使用 !clrstack 命令。
0:112> !clrstack OS Thread Id: 0x3278 (112) Child SP IP Call Site 000000b4ddc79098 00007ffe28b9fa74 [HelperMethodFrame: 000000b4ddc79098] 000000b4ddc791a0 00007ffda6c229cb System.Data.Entity.ModelConfiguration.Utilities.EdmPropertyPath.System.Collections.Generic.IEnumerable<System.Data.Entity.Core.Metadata.Edm.EdmProperty>.GetEnumerator() 000000b4ddc79200 00007ffe01a179eb System.Linq.Enumerable.SequenceEqual[[System.__Canon, mscorlib]](System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Collections.Generic.IEqualityComparer`1<System.__Canon>) 000000b4ddc79280 00007ffda6c2297e System.Data.Entity.ModelConfiguration.Configuration.Mapping.EntityMappingConfiguration+c__DisplayClass14.b__11(System.Data.Entity.Core.Mapping.ColumnMappingBuilder) 000000b4ddc792b0 00007ffe01a13f8f System.Linq.Enumerable.SingleOrDefault[[System.__Canon, mscorlib]](System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Func`2<System.__Canon,Boolean>) 000000b4ddc79330 00007ffda6c2087c System.Data.Entity.ModelConfiguration.Configuration.Mapping.EntityMappingConfiguration.Configure(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest, System.Data.Entity.Core.Metadata.Edm.EntityType, System.Data.Entity.Core.Mapping.StorageEntityTypeMapping ByRef, Boolean, Int32, Int32) 000000b4ddc79520 00007ffda6c20128 System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureUnconfiguredType(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest, System.Data.Entity.Core.Metadata.Edm.EntityType) 000000b4ddc795a0 00007ffda6c1ffaf System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureTablesAndConditions(System.Data.Entity.Core.Mapping.StorageEntityTypeMapping, System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest) 000000b4ddc79620 00007ffda6c055c0 System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.ConfigureEntityTypes(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest) 000000b4ddc79680 00007ffda6c05474 System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest) 000000b4ddc796d0 00007ffda69ae5c2 System.Data.Entity.DbModelBuilder.Build(System.Data.Entity.Core.Common.DbProviderManifest, System.Data.Entity.Infrastructure.DbProviderInfo) 000000b4ddc79740 00007ffda6649ccf System.Data.Entity.DbModelBuilder.Build(System.Data.Common.DbConnection) 000000b4ddc79780 00007ffda7b4b2d3 System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(System.Data.Entity.DbContext, System.Xml.XmlWriter) 000000b4ddc797c0 00007ffda7b4acbe Class125.smethod_0(System.Data.Entity.DbContext) 000000b4ddc79820 00007ffda7b4aba4 Class617.smethod_22(System.Data.Entity.DbContext) 000000b4ddc79860 00007ffda7b4aa90 Class617.smethod_27(System.Data.Entity.DbContext) 000000b4ddc798c0 00007ffda7b3e9ec DbContextExtensions.GetModel(System.Data.Entity.DbContext) 000000b4ddc79910 00007ffda7b3e49b Class124.smethod_0(System.Data.Entity.DbContext, System.String) 000000b4ddc79950 00007ffda7b3d6c3 Class486.smethod_3[[System.__Canon, mscorlib]](System.Data.Entity.DbContext, Z.BulkOperations.BulkOperation`1<System.__Canon>, System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Collections.Generic.List`1<System.Object>) 000000b4ddc79a00 00007ffda7b36871 DbContextExtensions.BulkInsert[[System.__Canon, mscorlib]](System.Data.Entity.DbContext, System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Action`1<Z.EntityFramework.Extensions.EntityBulkOperation`1<System.__Canon>>) 000000b4ddc79ab0 00007ffda7b32c65 xxx.EFRepository`1[[System.__Canon, mscorlib]].BulkInsert(System.__Canon[]) ...
從棧信息看,大概有如下三點(diǎn)信息:
- 正在用 EF 做批量插入操作 BulkInsert。
- 用了 Z.EntityFramework 插件。
- 大量的 Build, Configure 字樣,貌似是在做什么配置,構(gòu)建啥的。
3. 是插入數(shù)據(jù)過(guò)多導(dǎo)致的嗎?
第一個(gè)能想到的就是 list 過(guò)大,為了驗(yàn)證,可以用 !clrstack -a 把 BulkInsert 方法的 list 參數(shù)給導(dǎo)出來(lái)。
0:112> !clrstack -a OS Thread Id: 0x3278 (112) Child SP IP Call Site 000000b4ddc79b90 00007ffda7b31ee8 xxx.BLL.BaseService`1[[System.__Canon, mscorlib]].BulkInsert(System.__Canon[]) PARAMETERS: this (0x000000b4ddc79d10) = 0x000001fa14bbb630 _tArr (0x000000b4ddc79d18) = 0x000001fa14c1a2f8 0:112> !do 0x000001fa14c1a2f8 Name: xxx.EntityModel.xxx[] MethodTable: 00007ffda9437968 EEClass: 00007ffe02f556b0 Size: 56(0x38) bytes Array: Rank 1, Number of elements 4, Type CLASS (Print Array) Fields: None
從輸出看,當(dāng)前的list.length=4,這就很疑惑了,既然 heap 都在 balance ,那是不是有幾個(gè)線程在猛攻? 為了驗(yàn)證就用 DbContextExtensions.BulkInsert 在所有的托管線程棧上搜關(guān)鍵詞看看。
可以看到當(dāng)前有 10 處在猛攻,依次看他們的list都不大,疑惑哈??。
4. 對(duì)問(wèn)題的預(yù)判斷
有了這些思路,但總覺(jué)得觸發(fā)GC的由頭太怪了,不過(guò)可以肯定的是問(wèn)題出在了 Z.EntityFramework 插件上,按照 4S店的傳統(tǒng)經(jīng)驗(yàn),只換不修肯定沒(méi)問(wèn)題,由于我對(duì) Z.EntityFramework 不熟悉,也只能這樣給到朋友了。
說(shuō)來(lái)也奇怪,朋友第二天發(fā)現(xiàn)了一個(gè)奇怪現(xiàn)象,說(shuō)每次 CPU 爆高之前都出現(xiàn)了一次 w3wp的異常重啟,而重啟之后由于 Z.EntityFramework 需要預(yù)熱,導(dǎo)致后續(xù)請(qǐng)求阻塞引發(fā)的 CPU 階段性爆高。
從朋友的留言加上剛才的 dump 分析,問(wèn)題基本就能定位了, Build, Configure 和 banlance 操作都能解釋的通,而且還發(fā)現(xiàn)這個(gè)所謂的預(yù)熱并沒(méi)有做到串行化,而是10個(gè)線程一起來(lái),直到預(yù)熱結(jié)束,CPU 下降。
三:總結(jié)
總的來(lái)說(shuō),這次CPU階段性爆高的事故是由于 w3wp 進(jìn)程的意外重啟,導(dǎo)致多線程并發(fā)對(duì) Z.EntityFramework 預(yù)熱,在預(yù)熱的過(guò)程中導(dǎo)致了多次 GC 觸發(fā),至于 w3wp 為什么被意外終止,這就是另外一個(gè)話題了,不過(guò)好消息是朋友在后續(xù)的幾天中從抓取的 crash dump中找到了問(wèn)題代碼。
以上就是.NET CPU爆高事故事故分析某供應(yīng)鏈WEB網(wǎng)站的詳細(xì)內(nèi)容,更多關(guān)于.NET WEB網(wǎng)站CPU爆高的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Could not load file or assembly "App_Licenses.dll"
Could not load file or assembly "App_Licenses.dll"的問(wèn)題2010-03-03.net core下對(duì)于附件上傳下載的實(shí)現(xiàn)示例
本篇文章主要介紹了.net core下對(duì)于附件上傳下載的實(shí)現(xiàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03基于.NET BitmapImage 內(nèi)存釋放問(wèn)題的解決方法詳解
本篇文章是對(duì).NET BitmapImage 內(nèi)存釋放問(wèn)題的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C#調(diào)用動(dòng)態(tài)unlha32.dll解壓Lha后綴的打包文件分享
這篇文章介紹了,C#調(diào)用動(dòng)態(tài)unlha32.dll解壓Lha后綴的打包文件,有需要的朋友可以參考一下2013-09-09強(qiáng)烈推薦一個(gè)基于.Net Framework開(kāi)發(fā)的Windows右鍵菜單管理工具
這篇文章主要介紹了推薦一個(gè)基于.Net Framework開(kāi)發(fā)的Windows右鍵菜單管理工具,今天給大家推薦一個(gè)Windows右鍵菜單管理工具,方便我們管理我們的右鍵菜單,需要的朋友可以參考下2023-05-05