Aracılığıyla paylaş


.NET'te bellek sızıntısında hata ayıklama

Bu makale şunlar için geçerlidir: ✔️ .NET Core 3.1 SDK ve sonraki sürümler

Uygulamanız artık istenen görevi gerçekleştirmek için gerekmeyen nesnelere başvurduğunda bellek sızıntıya neden olabilir. Bu nesnelere başvurmak, çöp toplayıcının kullanılan belleği geri kazanmasını engeller. Bu, performans düşüşüne ve bir OutOfMemoryException istisnanın atılmasına yol açabilir.

Bu öğretici, .NET tanılama CLI araçlarını kullanarak bir .NET uygulamasındaki bellek sızıntısını analiz etme araçlarını gösterir. Windows kullanıyorsanız, bellek sızıntısında hata ayıklamak için Visual Studio'nun Bellek Tanılama araçlarını kullanabilirsiniz .

Bu öğreticide, alıştırma amacıyla bilinçli olarak bellek sızdıran bir örnek uygulama kullanılmaktadır. İstenmeyen bellek sızıntısı olan uygulamaları da analiz edebilirsiniz.

Bu kılavuzda aşağıdakileri yapacaksınız:

  • dotnet-counters ile yönetilen bellek kullanımını inceleyin.
  • Döküm dosyası oluşturun.
  • Döküm dosyasını kullanarak bellek kullanımını analiz edin.

Önkoşullar

Eğiticide şunlar kullanılıyor:

Kılavuzda, örnek uygulamaların ve araçların yüklendiği ve kullanıma hazır olduğu varsayılır.

Uygulamanız .NET 9'dan daha eski bir .NET sürümü çalıştırıyorsa dotnet-counters çıkış kullanıcı arabirimi biraz farklı görünür; Ayrıntılar için dotnet-counters bölümüne bakın.

Yönetilen bellek kullanımını inceleme

Bu senaryonun kök nedenini belirlemeye yardımcı olacak tanılama verilerini toplamaya başlamadan önce, gerçekten bir bellek sızıntısı (bellek kullanımında artış) gördüğünüzden emin olun. Bunu onaylamak için dotnet-counters aracını kullanabilirsiniz.

Bir konsol penceresi açın ve örnek hata ayıklama hedefini indirip sıkıştırmasını açtığınız dizine gidin. Hedefi çalıştırın:

dotnet run

Ayrı bir konsoldan işlem kimliğini bulun:

dotnet-counters ps

Çıkış şuna benzer olmalıdır:

4807 DiagnosticScena /home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios

Uyarı

Önceki komut çalışmazsa veya bulunamazsa, büyük olasılıkla önce aracı yüklemeniz dotnet-counters gerekir. Aşağıdaki komutu kullanın:

dotnet tool install --global dotnet-counters

Şimdi dotnet-counters aracıyla yönetilen bellek kullanımını denetleyin. --refresh-interval yenilemeler arasındaki saniye sayısını belirtir.

dotnet-counters monitor --refresh-interval 1 -p 4807

Canlı çıkış şuna benzer olmalıdır:

Press p to pause, r to resume, q to quit.
Status: Running

Name                                                            Current Value
[System.Runtime]
    dotnet.assembly.count ({assembly})                              111
    dotnet.gc.collections ({collection})
    gc.heap.generation
    ------------------
    gen0                                                              1
    gen1                                                              0
    gen2                                                              0
    dotnet.gc.heap.total_allocated (By)                       4,431,712
    dotnet.gc.last_collection.heap.fragmentation.size (By)
    gc.heap.generation
    ------------------
    gen0                                                        803,576
    gen1                                                         15,456
    gen2                                                              0
    loh                                                               0
    poh                                                               0
    dotnet.gc.last_collection.heap.size (By)
    gc.heap.generation
    ------------------
    gen0                                                        811,960
    gen1                                                      1,214,720
    gen2                                                              0
    loh                                                               0
    poh                                                          24,528
    dotnet.gc.last_collection.memory.committed_size (By)      4,296,704
    dotnet.gc.pause.time (s)                                          0.003
    dotnet.jit.compilation.time (s)                                   0.329
    dotnet.jit.compiled_il.size (By)                            120,212
    dotnet.jit.compiled_methods ({method})                            1,202
    dotnet.monitor.lock_contentions ({contention})                    2
    dotnet.process.cpu.count ({cpu})                                 22
    dotnet.process.cpu.time (s)
    cpu.mode
    --------
    system                                                            0.344
    user                                                              0.344
    dotnet.process.memory.working_set (By)                   64,331,776
    dotnet.thread_pool.queue.length ({work_item})                     0
    dotnet.thread_pool.thread.count ({thread})                        0
    dotnet.thread_pool.work_item.count ({work_item})                  7
    dotnet.timer.count ({timer})                                      0

Şu satıra odaklanın:

    dotnet.gc.last_collection.memory.committed_size (By)   4,296,704

Yönetilen yığın belleğinin başlangıçtan hemen sonra 4 MB olduğunu görebilirsiniz.

Şimdi URL'ye https://localhost:5001/api/diagscenario/memleak/20000gidin.

Bellek kullanımının 20 MB'ın üzerine geldiğini gözlemleyin.

    dotnet.gc.last_collection.memory.committed_size (By)   21,020,672

Bellek kullanımını izleyerek, belleğin arttığını veya sızdırıldığını güvenle söyleyebilirsiniz. Sonraki adım, bellek analizi için doğru verileri toplamaktır.

Bellek dökümü oluşturma

Olası bellek sızıntılarını analiz ederken, bellek içeriğini analiz etmek için uygulamanın bellek yığınına erişmeniz gerekir. Nesneler arasındaki ilişkilere baktığınızda, belleğin neden serbest kalmadığının teorilerini oluşturursunuz. Yaygın bir tanılama veri kaynağı, Windows'da bellek dökümü veya Linux'ta eşdeğer çekirdek dökümüdür. Bir .NET uygulamasının dökümünü oluşturmak için dotnet-dump aracını kullanabilirsiniz.

Daha önce başlatılan örnek hata ayıklama hedefini kullanarak bir Linux çekirdek dökümü oluşturmak için aşağıdaki komutu çalıştırın:

dotnet-dump collect -p 4807

Sonuç, aynı klasörde bulunan bir core dump'tır.

Writing minidump with heap to ./core_20190430_185145
Complete

Uyarı

Zaman içinde bir karşılaştırma için, ilk dökümü topladıktan sonra özgün işlemin çalışmaya devam etmesine ve ikinci bir dökümü aynı şekilde toplamasına izin verin. Daha sonra, belli bir süre boyunca karşılaştırabileceğiniz iki bellek dökümüne sahip olursunuz, bu da bellek kullanımının hangi alanlarda arttığını görmenizi sağlar.

Başarısız işlemi yeniden başlatma

Bellek dökümü toplandıktan sonra başarısız işlemi tanılamak için yeterli bilgiye sahip olmuş olacaksınız. Başarısız işlem bir üretim sunucusunda çalışıyorsa, işlemi yeniden başlatarak kısa vadeli düzeltme için ideal zamandır.

Bu öğreticide artık Örnek hata ayıklama hedefiyle işinizi bitirdiniz ve onu kapatabilirsiniz. Sunucuyu başlatan terminale gidin ve Ctrl+C tuşlarına basın.

Çekirdek dökümünü analiz et

Çekirdek dökümü oluşturduğunuza göre, dökümü analiz etmek için dotnet-dump aracını kullanın:

dotnet-dump analyze core_20190430_185145

Burada core_20190430_185145 , analiz etmek istediğiniz çekirdek dökümünün adıdır.

Uyarı

libdl.so bulunamadığını belirten bir hata görürseniz libc6-dev paketini yüklemeniz gerekebilir. Daha fazla bilgi için bkz. Linux üzerinde .NET önkoşulları.

SOS komutlarını girebileceğiniz bir istem gösterilir. Genellikle, bakmak istediğiniz ilk şey yönetilen yığının genel durumudur:

> dumpheap -stat

Statistics:
              MT    Count    TotalSize Class Name
...
00007f6c1eeefba8      576        59904 System.Reflection.RuntimeMethodInfo
00007f6c1dc021c8     1749        95696 System.SByte[]
00000000008c9db0     3847       116080      Free
00007f6c1e784a18      175       128640 System.Char[]
00007f6c1dbf5510      217       133504 System.Object[]
00007f6c1dc014c0      467       416464 System.Byte[]
00007f6c21625038        6      4063376 testwebapi.Controllers.Customer[]
00007f6c20a67498   200000      4800000 testwebapi.Controllers.Customer
00007f6c1dc00f90   206770     19494060 System.String
Total 428516 objects

Burada çoğu nesnenin String veya Customer nesne olduğunu görebilirsiniz.

Tüm dumpheap örneklerin String listesini almak için komutunu yöntem tablosuyla (MT) yeniden kullanabilirsiniz:

> dumpheap -mt 00007f6c1dc00f90

         Address               MT     Size
...
00007f6ad09421f8 00007faddaa50f90       94
...
00007f6ad0965b20 00007f6c1dc00f90       80
00007f6ad0965c10 00007f6c1dc00f90       80
00007f6ad0965d00 00007f6c1dc00f90       80
00007f6ad0965df0 00007f6c1dc00f90       80
00007f6ad0965ee0 00007f6c1dc00f90       80

Statistics:
              MT    Count    TotalSize Class Name
00007f6c1dc00f90   206770     19494060 System.String
Total 206770 objects

gcroot komutunu bir System.String örneğinde kullanarak nesnenin nasıl ve neden kök saldığını görebilirsiniz.

> gcroot 00007f6ad09421f8

Thread 3f68:
    00007F6795BB58A0 00007F6C1D7D0745 System.Diagnostics.Tracing.CounterGroup.PollForValues() [/_/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs @ 260]
        rbx:  (interior)
            ->  00007F6BDFFFF038 System.Object[]
            ->  00007F69D0033570 testwebapi.Controllers.Processor
            ->  00007F69D0033588 testwebapi.Controllers.CustomerCache
            ->  00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
            ->  00007F6C000148A0 testwebapi.Controllers.Customer[]
            ->  00007F6AD0942258 testwebapi.Controllers.Customer
            ->  00007F6AD09421F8 System.String

HandleTable:
    00007F6C98BB15F8 (pinned handle)
    -> 00007F6BDFFFF038 System.Object[]
    -> 00007F69D0033570 testwebapi.Controllers.Processor
    -> 00007F69D0033588 testwebapi.Controllers.CustomerCache
    -> 00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
    -> 00007F6C000148A0 testwebapi.Controllers.Customer[]
    -> 00007F6AD0942258 testwebapi.Controllers.Customer
    -> 00007F6AD09421F8 System.String

Found 2 roots.

String nesnesinin Customer nesnesi tarafından doğrudan, CustomerCache nesnesi tarafından dolaylı olarak tutulduğunu görebilirsiniz.

Çoğu nesnenin benzer bir desen izlediğini görmek için nesneleri dökmeye devam edebilirsiniz. Bu noktada araştırma, kodunuzdaki kök nedeni tanımlamak için yeterli bilgi sağladı.

Bu genel yordam, ana bellek sızıntılarının kaynağını belirlemenize olanak tanır.

Kaynakları temizle

Bu öğreticide örnek bir web sunucusu başlattınız. Bu sunucu , Başarısız işlemi yeniden başlatma bölümünde açıklandığı gibi kapatılmış olmalıdır.

Ayrıca, oluşturulan döküm dosyasını silebilirsiniz.

Ayrıca bakınız

Sonraki Adımlar