成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

PerfView 洞察 C#托管堆內存 "黑洞現象"

開發 前端
內存黑洞 雖不算 CLR 的一個bug,但絕對是 CLR 可優化的一個空間,分析這類問題是需要經驗性的,分享出來供后來者少踩坑吧,畢竟在我的分析旅程中至少遇到了3次。

一:背景

1. 講故事

首先聲明的是這個 黑洞 是我定義的術語,它是用來表示 內存吞噬 的一種現象,何為 內存吞噬,我們來看一張圖。

圖片圖片

從上面的 卦象圖 來看,GCHeap 的 Allocated=852M 和 Committed=16.6G,它們的差值就是 分配緩沖區=16G,緩沖區的好處就是用空間換時間,弊端就是會實實在在的侵占內存,擠壓其他程序的生存空間。

二:黑洞現象

1. 為什么會有黑洞現象

萬事皆有因果,今生的果是前世種的因,換句話說是程序曾經有大量及頻繁的創建臨時對象,讓GC不自主的痙攣,小攣傷神,大攣傷身,所以GC為了避免大攣的發生,就大量的囤積本應該釋放掉的內存,目的就是防止未來某個時刻再次有大內存分配的發生。

2. 重現今生的果

我相信因果關系大家都弄清楚了,但口說無憑,還得用代碼證明一下不是?為了模擬GC痙攣,上一段測試代碼。

public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddAuthorization();
            var app = builder.Build();

            // Configure the HTTP request pipeline.
            app.UseAuthorization();

            app.MapGet("/mytest", (HttpContext httpContext) =>
            {
                return MyTest();
            });

            app.MapGet("/gc", (HttpContext httpContext) =>
            {
                GC.Collect();

                return 1;
            });

            app.Run();
        }

        public static string MyTest()
        {
            List<string> list = new List<string>();

            for (int i = 0; i < 100000000; i++)
            {
                list.Add(i.ToString());
            }

            return "ok";
        }
    }

代碼非常簡單,每請求一次 /mytest 都會分配一個 1億 大小 List<string> 數組,而這個 List<string> 又是一個臨時對象,后續會被 GC 回收,接下來我們多請求幾次來調戲一下 GC,看他如何痙攣,截圖如下:

圖片圖片

從卦中看,我當前請求了 6 次,內存峰值達到了 12G,因為是臨時對象,稍稍有一點回落,但此時已經撐成一個大胖子了,接下來我們用 WinDbg 附加一下,觀察下 Allocated 和 Committed 閾值。

0:033> !eeheap -gc

========================================
Number of GC Heaps: 12
----------------------------------------
...
Heap 11 (0000023513f26c10)
generation 0 starts at 23351c3aab8
generation 1 starts at 233484c38e0
generation 2 starts at 233484c1000
ephemeral segment allocation context: none
Small object heap
         segment            begin        allocated        committed allocated size          committed size         
    0233484c0000     0233484c1000     02335c794ad0     023379ad2000 0x142d3ad0 (338508496)  0x31612000 (828448768) 
Large object heap starts at 234384c1000
         segment            begin        allocated        committed allocated size          committed size         
    0234384c0000     0234384c1000     0234384c1018     0234384e2000 0x18 (24)               0x22000 (139264)       
Pinned object heap starts at 234f84c1000
         segment            begin        allocated        committed allocated size          committed size         
    0234f84c0000     0234f84c1000     0234f84c1018     0234f84c2000 0x18 (24)               0x2000 (8192)          
------------------------------
GC Allocated Heap Size:    Size: 0x14f241378 (5622731640) bytes.
GC Committed Heap Size:    Size: 0x2b125c000 (11561975808) bytes.

從卦中看當前已經有 6G 的緩沖區了,為了讓緩沖區更夸張,我們故意手工觸發一次 GC 即請求 /gc,觸發了GC之后,內存從 10G 回落到了 7G 就不再降了,截圖如下:

圖片圖片

從卦中看,這兩個指標就更夸張了,GC 堆只有 1.1M 的對象,但預留了 7.1G 的內存。

這個GC表現不管在 道德 還是 倫理 上都說不通的。

3. 找到前世的因

要想找到前世的因,手段有很多,比如用 WinDbg 觀察前世的托管堆,從殘留的 Committed - Allocated上就能找到因,也可以使用 PerfView 實時觀察,這里我們采用后者來洞察,使用默認的 Command 參數。

PerfView.exe  "/DataFile:PerfViewData.etl" /BufferSizeMB:256 /StackCompression /CircularMB:500 /ClrEvents:GC,Binder,Security,AppDomainResourceManagement,Contention,Exception,Threading,JITSymbols,Type,GCHeapSurvivalAndMovement,GCHeapAndTypeNames,Stack,ThreadTransfer,Codesymbols,Compilation /NoGui /NoNGenRundown /Merge:True /Zip:True collect

采集一段時間后停止采集,接下來雙擊 GC Heap Net Mem (Coarse Sampling) Stacks 選項再選擇 WebApplication1 進程,通過 MaxMetric 指標看到曾經峰值達到了 10.9G,截圖如下:

圖片圖片

毫無疑問的說,內存峰值的時候必有妖怪,可以將峰值填入到 End 文本框中,然后雙擊內存占比最高的 System.String[],觀察下它是誰分配的,截圖如下:

圖片圖片

從截圖中可以清晰的看到,原來是 Program.MyTest() 造的孽,至此真相大白。

4. 尋求化解之道

化解之道有很多:

  • 修改 GC 模式

簡而言之就是將 Server GC 改成 Workstation GC ,參考代碼如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <ServerGarbageCollection>false</ServerGarbageCollection>
  </PropertyGroup>

</Project>
  • 修改 Heap 個數

默認情況一個 cpucore 有一個 heap,我們可以盡量的減少 heap.count 的個數,比如將 12 個改成 2 個。參考代碼如下:

{
   "runtimeOptions": {
      "configProperties": {
         "System.GC.HeapCount": 2
      }
   }
}
  • 大事化小

導致今世的果 是因為在內存中短時的出現大對象,可以將大對象拆分成多批次的小對象處理,這樣可以達到后浪推前浪的的內存復用,從源頭上繞過這個問題。

三:總結

內存黑洞 雖不算 CLR 的一個bug,但絕對是 CLR 可優化的一個空間,分析這類問題是需要經驗性的,分享出來供后來者少踩坑吧,畢竟在我的分析旅程中至少遇到了3次。

責任編輯:武曉燕 來源: 一線碼農聊技術
相關推薦

2023-07-17 11:25:35

.NET程序WinDbgPerfview

2023-11-01 08:07:42

.NETC#

2024-06-12 09:16:23

2022-08-26 00:00:01

C#內存PerfView

2009-08-19 10:25:18

C#托管資源

2009-09-02 16:02:52

C#引用托管對象

2009-09-02 10:39:00

C#釋放托管資源

2009-08-28 16:43:08

AutoCAD托管C#

2011-05-18 18:05:47

C#C++

2011-05-18 17:56:38

C#C++

2023-07-07 13:56:54

2009-08-17 13:49:20

C#中調用Window

2009-08-25 09:49:09

C#內存Graphic

2009-08-28 10:14:45

C#內存泄露

2009-09-03 16:58:49

C#內存管理

2009-08-20 11:01:51

C#操作內存

2010-01-25 15:55:50

托管C++

2009-08-20 10:25:37

C#操作內存

2011-05-20 15:37:05

MemoryStrea

2009-08-20 10:53:23

C#操作內存
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧洲视频一区二区 | 超碰精品在线观看 | 蜜桃av一区二区三区 | 午夜电影日韩 | 国产日产久久高清欧美一区 | 国产高清免费 | 日韩av在线一区二区 | 欧美国产日韩在线 | 久久精品国产一区老色匹 | 婷婷久久综合 | 国产成人免费视频网站视频社区 | 亚洲成人999 | 精品久久久久久久久久 | 男女精品网站 | 色资源av| 黄a在线播放 | 天天干天天玩天天操 | 免费一级淫片aaa片毛片a级 | 日本不卡一二三 | 综合色播 | 在线成人 | 精品国产乱码久久久久久牛牛 | 国产精品毛片一区二区在线看 | 在线播放中文字幕 | 国产电影一区二区在线观看 | 国产精品久久久久一区二区三区 | 午夜ww | 亚洲精品福利在线 | 国产精品成人一区二区 | 蜜桃一区二区三区 | 国产成人精品一区二区三区四区 | 毛片链接 | 超碰免费观看 | 欧美男人天堂 | 久久午夜影院 | 日韩福利在线观看 | 日本不卡高字幕在线2019 | 欧美精品久久久久 | 国际精品鲁一鲁一区二区小说 | 欧美黑人一级爽快片淫片高清 | 成人影院在线视频 |