İngilizce dilinde oku

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üşüşü ve özel durum OutOfMemoryException oluşturmayla sonuçlanabilir.

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 olarak kasıtlı olarak bellek sızdıran örnek bir uygulama kullanılmaktadır. İstenmeyen bellek sızıntısı olan uygulamaları da analiz edebilirsiniz.

Bu öğreticide şunları 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.

Ön koşullar

Öğreticide aşağıdakiler kullanılır:

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

Yönetilen bellek kullanımını inceleme

Bu senaryoya kök neden olacak tanılama verilerini toplamaya başlamadan önce gerçekten bir bellek sızıntısı (bellek kullanımında büyüme) 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:

.NET CLI
dotnet run

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

Console
dotnet-counters ps

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

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

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

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

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

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

[System.Runtime]
    # of Assemblies Loaded                           118
    % Time in GC (since last GC)                       0
    Allocation Rate (Bytes / sec)                 37,896
    CPU Usage (%)                                      0
    Exceptions / sec                                   0
    GC Heap Size (MB)                                  4
    Gen 0 GC / sec                                     0
    Gen 0 Size (B)                                     0
    Gen 1 GC / sec                                     0
    Gen 1 Size (B)                                     0
    Gen 2 GC / sec                                     0
    Gen 2 Size (B)                                     0
    LOH Size (B)                                       0
    Monitor Lock Contention Count / sec                0
    Number of Active Timers                            1
    ThreadPool Completed Work Items / sec             10
    ThreadPool Queue Length                            0
    ThreadPool Threads Count                           1
    Working Set (MB)                                  83

Şu satıra odaklanın:

Console
    GC Heap Size (MB)                                  4

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 30 MB'a kadar arttığını gözlemleyin.

Console
    GC Heap Size (MB)                                 30

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:

.NET CLI
dotnet-dump collect -p 4807

Sonuç, aynı klasörde bulunan çekirdek dökümüdür.

Console
Writing minidump with heap to ./core_20190430_185145
Complete

Not

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, bellek kullanımının nerede arttığını görmek için karşılaştırabileceğiniz bir süre boyunca iki döküme sahip olursunuz.

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

Döküm toplandıktan sonra başarısız işlemi tanılamak için yeterli bilgiye sahip olmanız gerekir. 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şiniz bitti ve bunu kapatabilirsiniz. Sunucuyu başlatan terminale gidin ve Ctrl+C tuşlarına basın.

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

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

.NET CLI
dotnet-dump analyze core_20190430_185145

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

Not

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:

Console
> 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 veya Customer nesne olduğunu String görebilirsiniz.

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

Console
> 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

Şimdi bir örnekte komutunu kullanarak nesnenin gcrootSystem.String nasıl ve neden köklendiğini görebilirsiniz:

Console
> 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.

öğesinin String nesne tarafından Customer doğrudan tutulduğunu ve dolaylı olarak bir CustomerCache nesne tarafından tutulduğunu görebilirsiniz.

Çoğu nesnenin benzer bir desen izlediğini görmek String için nesnelerin dökümünü almaya 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ı temizleme

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 bkz.

Sonraki adımlar