## 一:背景 ### 1. 講故事 前段時間有位朋友找到我,說他程式CPU直接被打滿了,讓我幫忙看下怎麼回事,截圖如下: ![](https://img2023.cnblogs.com/blog/214741/202307/214741-20230731153115090-546047217.pn ...
一:背景
1. 講故事
前段時間有位朋友找到我,說他程式CPU直接被打滿了,讓我幫忙看下怎麼回事,截圖如下:
看了下是兩個相同的程式,既然被打滿了那就抓一個 dump 看看到底咋回事。
二:為什麼會打滿
1. 真的被打滿了嗎
凡事都要用數據說話,我們使用 !tp
命令觀察一下。
0:014> !tp
logStart: 62
logSize: 200
CPU utilization: 100 %
Worker Thread: Total: 16 Running: 0 Idle: 16 MaxLimit: 32767 MinLimit: 8
Work Request in Queue: 0
--------------------------------------
Number of Timers: 8
--------------------------------------
Completion Port Thread:Total: 9 Free: 2 MaxFree: 16 CurrentLimit: 9 MaxLimit: 1000 MinLimit: 8
從卦象看果然是被打滿了,那為什麼會滿呢?一般來說CPU高是線程抬起來的,接下來我們就從線程入手。
2. 線程都在做什麼事情
要想觀察每個線程都在做什麼,可以使用 ~*e !clrstack
命令,打完所有的線程棧後,明顯發現有 6 處在 System.Text.RegularExpressions.RegexReplacement.Replace
正則替換這裡,截圖如下:
0:021> ~14s
ntdll!NtWaitForSingleObject+0x14:
00007ff9`c5d4fa74 c3 ret
0:014> !clrstack
OS Thread Id: 0x6ee0 (14)
Child SP IP Call Site
000000AC6CBF99C8 00007ff9c5d4fa74 [HelperMethodFrame: 000000ac6cbf99c8]
000000AC6CBF9AC0 00007ff942416c05 System.String.Create[[System.Text.SegmentStringBuilder, System.Text.RegularExpressions]](Int32, System.Text.SegmentStringBuilder, System.Buffers.SpanAction`2<Char,System.Text.SegmentStringBuilder>)
000000AC6CBF9B20 00007ff942416aeb System.Text.SegmentStringBuilder.ToString()
000000AC6CBF9BA0 00007ff9422e62ac System.Text.RegularExpressions.RegexReplacement.Replace(System.Text.RegularExpressions.Regex, System.String, Int32, Int32)
000000AC6CBF9C70 00007ff9422e4ec6 System.Text.RegularExpressions.Regex.Replace(System.String, System.String, System.String, System.Text.RegularExpressions.RegexOptions)
000000AC6CBF9CD0 00007ff941e157aa SqlSugar.UtilMethods.ReplaceSqlParameter(System.String, SqlSugar.SugarParameter, System.String)
000000AC6CBF9F80 00007ff941e42990 SqlSugar.SqlSugarProvider+d__245`1[[System.Int32, System.Private.CoreLib]].MoveNext()
000000AC6CBFA300 00007ff94190e93c System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[System.__Canon, System.Private.CoreLib]](System.__Canon ByRef)
000000AC6CBFA360 00007ff941e420bd SqlSugar.SqlSugarProvider.SaveQueuesProviderAsync[[System.Int32, System.Private.CoreLib]](Boolean, System.Func`3<System.String,System.Collections.Generic.List`1<SqlSugar.SugarParameter>,System.Threading.Tasks.Task`1>)
000000AC6CBFA3D0 00007ff941e41a52 SqlSugar.SqlSugarProvider+d__224.MoveNext()
000000AC6CBFA480 00007ff94190e93c System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[System.__Canon, System.Private.CoreLib]](System.__Canon ByRef)
000000AC6CBFA4E0 00007ff941e418f4 SqlSugar.SqlSugarProvider.SaveQueuesAsync(Boolean)
000000AC6CBFA550 00007ff941e417fe SqlSugar.SqlSugarClient.SaveQueuesAsync(Boolean)
000000AC6CBFA5A0 00007ff941e4177e SqlSugar.SqlSugarScope.SaveQueuesAsync(Boolean)
000000AC6CBFA5F0 00007ff941e40fce xxx.Repository.BaseRepository`1+d__76[[System.__Canon, System.Private.CoreLib]].MoveNext()
...
000000AC6D4FAAF0 00007ff9422c9d0c xxx.xxxService+d__15.MoveNext()
...
從上面的 MoveNext 和 AsyncMethodBuilder 來看,這裡用的是全非同步寫法,分析起來那是一個頭大哈。。。不過仔細觀察是 SqlSugar 在替換sql參數的時候引發的,一般來說和 Regular
有關的操作都是蠻耗 CPU 的,然後順手看了下cpu配置也才 8 核,難怪 CPU 直接 100% 了。
0:014> !cpuid
CP F/M/S Manufacturer MHz
0 6,85,7 <unavailable> 2500
1 6,85,7 <unavailable> 2500
2 6,85,7 <unavailable> 2500
3 6,85,7 <unavailable> 2500
4 6,85,7 <unavailable> 2500
5 6,85,7 <unavailable> 2500
6 6,85,7 <unavailable> 2500
7 6,85,7 <unavailable> 2500
3. SqlSugar 到底在做什麼
要想知道做什麼,逆向一下代碼就好,截圖如下:
這種寫法好不好我就不評價了,至少簡單粗暴,那為什麼會很耗時呢?這就要扒一下 ReplaceSqlParameter
方法中的三個參數,尤其是 itemSql
欄位,然後使用 !clrstack -a
。
0:014> !clrstack -a
OS Thread Id: 0x6ee0 (14)
Child SP IP Call Site
000000AC6CBF9CD0 00007ff941e157aa SqlSugar.UtilMethods.ReplaceSqlParameter(System.String, SqlSugar.SugarParameter, System.String)
PARAMETERS:
itemSql (0x000000AC6CBF9F80) = 0x0000023d802e1020
itemParameter (0x000000AC6CBF9F88) = 0x0000023c4bd3ae58
newName (0x000000AC6CBF9F90) = 0x0000023ca9dd3328
LOCALS:
0x000000AC6CBF9F68 = 0x0000000000000000
0:014> !do 0x0000023d802e1020
Name: System.String
MethodTable: 00007ff93caad698
EEClass: 00007ff93ca89d60
Tracked Type: false
Size: 21391508(0x1466894) bytes
File: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.12\System.Private.CoreLib.dll
String: <String is invalid or too large to print>
Fields:
MT Field Offset Type VT Attr Value Name
00007ff93ca99480 40002f2 8 System.Int32 1 instance 10695743 _stringLength
00007ff93c9fea10 40002f3 c System.Char 1 instance 49 _firstChar
00007ff93caad698 40002f1 e8 System.String 0 static 0000023c3f5613a0 Empty
0:014> ?0n21391508 /0x400
Evaluate expression: 20890 = 00000000`0000519a
從卦中看,簡直是嚇一跳,這個 sql 居然高達 20M,