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

PerfView 洞察 C#托管堆內(nèi)存 "黑洞現(xiàn)象"

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

一:背景

1. 講故事

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

圖片圖片

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

二:黑洞現(xiàn)象

1. 為什么會有黑洞現(xiàn)象

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

2. 重現(xiàn)今生的果

我相信因果關(guān)系大家都弄清楚了,但口說無憑,還得用代碼證明一下不是?為了模擬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> 數(shù)組,而這個 List<string> 又是一個臨時對象,后續(xù)會被 GC 回收,接下來我們多請求幾次來調(diào)戲一下 GC,看他如何痙攣,截圖如下:

圖片圖片

從卦中看,我當(dāng)前請求了 6 次,內(nèi)存峰值達(dá)到了 12G,因為是臨時對象,稍稍有一點回落,但此時已經(jīng)撐成一個大胖子了,接下來我們用 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.

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

圖片圖片

從卦中看,這兩個指標(biāo)就更夸張了,GC 堆只有 1.1M 的對象,但預(yù)留了 7.1G 的內(nèi)存。

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

3. 找到前世的因

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

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 指標(biāo)看到曾經(jīng)峰值達(dá)到了 10.9G,截圖如下:

圖片圖片

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

圖片圖片

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

4. 尋求化解之道

化解之道有很多:

  • 修改 GC 模式

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

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

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

</Project>
  • 修改 Heap 個數(shù)

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

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

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

三:總結(jié)

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

責(zé)任編輯:武曉燕 來源: 一線碼農(nóng)聊技術(shù)
相關(guān)推薦

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#內(nèi)存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++

2009-08-17 13:49:20

C#中調(diào)用Window

2023-07-07 13:56:54

2009-08-25 09:49:09

C#內(nèi)存Graphic

2009-08-28 10:14:45

C#內(nèi)存泄露

2009-09-03 16:58:49

C#內(nèi)存管理

2009-08-20 11:01:51

C#操作內(nèi)存

2010-01-25 15:55:50

托管C++

2009-08-20 10:25:37

C#操作內(nèi)存

2011-05-20 15:37:05

MemoryStrea

2009-08-20 10:53:23

C#操作內(nèi)存
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 91福利电影在线观看 | 亚洲一区二区三区免费视频 | 欧美特级黄色 | 日本三级电影免费 | 国产一级片免费在线观看 | 91免费电影 | 国产伦精品一区二区三区照片91 | 丁香五月网久久综合 | 国产一区在线免费观看视频 | 日韩免费毛片视频 | 九色在线观看 | 亚洲综合二区 | 国产伦一区二区三区 | 九九九久久国产免费 | 一区二区日韩 | 欧美视频中文字幕 | 国产美女永久免费无遮挡 | 欧美日韩一区在线 | 亚洲国产精品人人爽夜夜爽 | 精品九九 | 午夜免费网站 | 日一区二区三区 | 亚洲一区二区高清 | 我想看一级黄色毛片 | 日韩欧美在线不卡 | 国产精品久久国产精品 | 免费国产视频 | 久久综合香蕉 | 99re6在线视频精品免费 | 91免费在线视频 | 国产精品无码专区在线观看 | 午夜影院在线视频 | 韩国av网站在线观看 | 日韩精品一区在线 | 亚洲成人激情在线观看 | 91国内视频在线 | 天天草草草 | 激情免费视频 | 中文字幕一区二区三区精彩视频 | 视频一二三区 | 精品蜜桃一区二区三区 |