<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      記一次 .NET 某企業ECM內容管理系統 內存暴漲分析

      一:背景

      1. 講故事

      這個dump是去年一個朋友發給我的,讓我幫忙分析下為什么內存暴漲,當時由于相關知識的缺乏,分析了一天也沒找出最后的原因,最后就不了了之的,直到最近我研究了下 CancellationTokenCompositeChangeToken 的底層玩法,才對這個問題有了新的視角,這篇就算是遲來的解讀吧。

      二:內存暴漲分析

      1. 為什么會暴漲

      由于是在 linux 上采摘下來的dump,所以用 !maddress -summary 命令觀察進程的內存布局,輸出如下:

      
       +-------------------------------------------------------------------------+ 
       | Memory Type            |          Count |         Size |   Size (bytes) | 
       +-------------------------------------------------------------------------+ 
       | Stack                  |          1,101 |       8.67gb |  9,305,092,096 | 
       | PAGE_READWRITE         |          1,371 |       1.13gb |  1,216,679,936 | 
       | GCHeap                 |             64 |     790.70mb |    829,108,224 | 
       | Image                  |          1,799 |     257.44mb |    269,944,832 | 
       | HighFrequencyHeap      |            797 |      49.85mb |     52,269,056 | 
       | LowFrequencyHeap       |            558 |      38.32mb |     40,185,856 | 
       | LoaderCodeHeap         |             23 |      33.53mb |     35,155,968 | 
       | HostCodeHeap           |             15 |       2.63mb |      2,752,512 | 
       | ResolveHeap            |              2 |     732.00kb |        749,568 | 
       | DispatchHeap           |              2 |     452.00kb |        462,848 | 
       | IndirectionCellHeap    |              5 |     280.00kb |        286,720 | 
       | PAGE_READONLY          |            124 |     253.50kb |        259,584 | 
       | CacheEntryHeap         |              4 |     228.00kb |        233,472 | 
       | LookupHeap             |              4 |     208.00kb |        212,992 | 
       | PAGE_EXECUTE_WRITECOPY |              5 |      48.00kb |         49,152 | 
       | StubHeap               |              1 |      12.00kb |         12,288 | 
       | PAGE_EXECUTE_READ      |              1 |       4.00kb |          4,096 | 
       +-------------------------------------------------------------------------+ 
       | [TOTAL]                |          5,876 |      10.95gb | 11,753,459,200 | 
       +-------------------------------------------------------------------------+ 
      
      

      這卦象一看嚇一跳,總計內存 10.95G,Stack就獨吃 8.67G,并且 Count=1101 也表明了當前有 1101 個線程,這么高的線程數一般也表示出大問題了。。。

      2. 為什么線程數這么高

      要想找到這個答案,可以用 ~*e !clrstack 觀察每個線程都在做什么,發現有大量的 Sleep 等待,輸出如下:

      
      0:749> ~*e !clrstack
      ...
      OS Thread Id: 0x6297 (932)
              Child SP               IP Call Site
      00007FE9D7FBB508 00007ffa5f564e2b [HelperMethodFrame: 00007fe9d7fbb508] System.Threading.Thread.SleepInternal(Int32)
      00007FE9D7FBB650 00007ff9e9ac113f System.Threading.SpinWait.SpinOnceCore(Int32) [/_/src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs @ 242]
      00007FE9D7FBB6E0 00007ff9ee55ffd8 System.Threading.CancellationTokenSource.WaitForCallbackToComplete(Int64) [/_/src/System.Private.CoreLib/shared/System/Threading/CancellationTokenSource.cs @ 804]
      00007FE9D7FBB710 00007ff9eea0817d Microsoft.Extensions.Primitives.CompositeChangeToken.OnChange(System.Object) [/_/src/libraries/Microsoft.Extensions.Primitives/src/CompositeChangeToken.cs @ 128]
      00007FE9D7FBB760 00007ff9e9adc75d System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean) [/_/src/System.Private.CoreLib/shared/System/Threading/CancellationTokenSource.cs @ 724]
      00007FE9D7FBB7D0 00007ff9e9ab8d61 System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 315]
      00007FE9D7FBB810 00007ff9e9abd8dc System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread) [/_/src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs @ 2421]
      00007FE9D7FBB890 00007ff9e9ab1039 System.Threading.ThreadPoolWorkQueue.Dispatch() [/_/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs @ 699]
      00007FE9D7FBBCA0 00007ffa5e6632df [DebuggerU2MCatchHandlerFrame: 00007fe9d7fbbca0] 
      ...
      
      

      仔細閱讀卦中的代碼,大概知道問題出在了 CompositeChangeToken.OnChange 里,接下來翻一遍源代碼,輸出如下:

      
      private static void OnChange(object state)
      {
          CompositeChangeToken compositeChangeToken = (CompositeChangeToken)state;
          if (compositeChangeToken._cancellationTokenSource == null)
          {
              return;
          }
          lock (compositeChangeToken._callbackLock)
          {
              try
              {
                  compositeChangeToken._cancellationTokenSource.Cancel();
              }
              catch
              {
              }
          }
          List<IDisposable> disposables = compositeChangeToken._disposables;
          for (int i = 0; i < disposables.Count; i++)
          {
              disposables[i].Dispose();
          }
      }
      
      private void WaitForCallbackIfNecessary()
      {
          CancellationTokenSource source = _node.Partition.Source;
          if (source.IsCancellationRequested && !source.IsCancellationCompleted && source.ThreadIDExecutingCallbacks != Environment.CurrentManagedThreadId)
          {
              source.WaitForCallbackToComplete(_id);
          }
      }
      
      internal void WaitForCallbackToComplete(long id)
      {
          SpinWait spinWait = default(SpinWait);
          while (ExecutingCallback == id)
          {
              spinWait.SpinOnce();
          }
      }
      
      

      上面的代碼可能有些人看不懂是什么意思,我先補充一下序列圖。

      接下來根據代碼將上面的序列化圖落地一下

      1. 自定義Token在哪里?

      這個可以深挖 CallbackNode 中的 CallbackState 字段,可以看到是 CancellationChangeToken ,截圖如下:

      1. OnChange 觸發在哪里

      根據 CompositeChangeToken 底層機制,這個組合變更令牌 在所有的子Token中都是共享的,在各個線程中我們都能看得到,截圖如下:

      1. CancellationTokenRegistration 在哪里

      這個類是我們回調函數的登記類,從 compositeChangeToken._disposables 中大概知道有 4 個回調函數,截圖如下:

      接下來將 dump 拖到 vs 中,觀察發現都卡死在 for 對 Dispose 遍歷上,截圖如下:

      為什么都會卡死在 disposables[i].Dispose(); 上?這是我們接下來要探究的問題,根據上面代碼中的 ThreadIDExecutingCallbacks != Environment.CurrentManagedThreadIdExecutingCallback == id 大概也能猜出來, A線程 要釋放的節點正在被 B線程 持有,可能 B線程 要釋放的節點正在被 A線程 持有,所以大概率引發了死鎖情況。。。

      3. 真的是死鎖嗎

      要想找到是不是真的發生了死鎖,可以由果推因將四個自定義的Token下的 CancellationChangeToken.cts.ThreadIDExecutingCallbacks 字段給找到,截圖如下:

      從卦中可以看到四個節點分別被 726,697,722,774 這4個線程持有,接下來切到 726號線程看下它此時正在做什么,截圖如下:

      從卦中可以看到726號線程已持有 disposables[0] ,正等待 697號線程持有的 disposables[1] 釋放,接下來切到 697號線程,看下它此時正在做什么,截圖如下:

      從卦中可以看到,697號線程持有 disposables[1] ,正等待 726 號線程持有的 disposables[0] 釋放。

      到這里就呈現出了經典的的死鎖!

      4. 為什么會出現死鎖

      很顯然這個死鎖是多線程操控共享的 compositeChangeToken.disposables[] 數組導致的,而且據當時朋友反饋并沒有用戶代碼故意為之,現在回頭看應該是 NET 3.1.20 內部的bug導致的。

      
      0:749> lmDvmlibcoreclr
      
          Image path: /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.20/libcoreclr.so
      
      

      為了驗證這個這個說法,我使用了最新的 .NET 3.1.32 版本,發現這里多了一個 if 判斷,截圖如下:

      不要小看這里面的if,因為一旦有人執行了 compositeChangeToken._cancellationTokenSource.Cancel() 方法,那么 compositeChangeToken._cancellationTokenSource.IsCancellationRequested 必然就是 true,可以避免后續有人無腦的對 disposables 遍歷。

      所以最好的辦法就是升級 coreclr 版本觀察。

      三:總結

      在高級調試的旅程中,會遇到各種 牛鬼蛇神,奇奇怪怪,不可思議的奇葩問題,玩.NET高級調試并不是能 fix bug,但確實能真真切切的縮小包圍圈,畢竟解鈴還須系鈴人!

      圖片名稱
      posted @ 2025-09-11 11:18  一線碼農  閱讀(613)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 色综合久久中文综合久久激情| 国产精品爽爽va在线观看网站| 日夜啪啪一区二区三区| 久久精品国产精品亚洲综合| 国精产品一区一区三区mba下载| 亚洲综合无码日韩国产加勒比 | 国内揄拍国内精品人妻| 国产精品普通话国语对白露脸| 九九视频热最新在线视频| 国产成人无码aa精品一区| 免费无码中文字幕A级毛片| 日韩有码精品中文字幕| 国产精品一区中文字幕| 夜夜爽77777妓女免费看| 亚洲欧美激情在线一区| 国产精品亚洲国际在线看| 2021亚洲国产精品无码| 越南女子杂交内射bbwxz| 久久人妻国产精品| 极品一区二区三区水蜜桃| 永久免费在线观看蜜桃视频| 精品无码三级在线观看视频 | 色婷婷五月综合久久| 亚洲欧美日韩在线码| 国产成人av乱码在线观看| 国产精品揄拍一区二区久久| 性一交一乱一伦| 麻豆tv入口在线看| 亚洲精品成人老司机影视| 中文字幕理伦午夜福利片| 看全色黄大色黄大片 视频| 精品人妻少妇一区二区三区| 免费无码午夜福利片| 无码中文字幕人妻在线一区| 国产视频一区二区三区麻豆| 日本高清在线播放一区二区三区| 黄男女激情一区二区三区| 国产精品成人午夜久久| 潜江市| 日韩va中文字幕无码电影| 欧美老少配性行为|