Men-debug kebocoran memori di .NET
Artikel ini berlaku untuk: ✔️ .NET Core 3.1 SDK dan versi yang lebih baru
Memori dapat bocor saat aplikasi Anda mereferensikan objek yang tidak lagi perlu melakukan tugas yang diinginkan. Mereferensikan objek ini mencegah pengumpul sampah mendapatkan kembali memori yang digunakan. Itu dapat mengakibatkan penurunan performa dan OutOfMemoryException pengecualian dilemparkan.
Tutorial ini menunjukkan alat untuk menganalisis kebocoran memori di aplikasi .NET menggunakan alat CLI diagnostik .NET. Jika Menggunakan Windows, Anda mungkin dapat menggunakan alat Diagnostik Memori Visual Studio untuk men-debug kebocoran memori.
Tutorial ini menggunakan aplikasi sampel yang sengaja membocorkan memori, sebagai latihan. Anda juga dapat menganalisis aplikasi yang secara tidak sengaja membocorkan memori.
Dalam tutorial ini, Anda akan:
- Periksa penggunaan memori terkelola dengan penghitung dotnet.
- Buat file cadangan.
- Analisis penggunaan memori menggunakan file cadangan.
Prasyarat
Tutorial ini menggunakan:
- .NET Core 3.1 SDK atau versi yang lebih baru.
- penghitung dotnet untuk memeriksa penggunaan memori terkelola.
- dotnet-dump untuk mengumpulkan dan menganalisis file cadangan (termasuk ekstensi debugging SOS).
- Contoh aplikasi target debug untuk didiagnosis.
Tutorial mengasumsikan aplikasi dan alat sampel diinstal dan siap digunakan.
Memeriksa penggunaan memori terkelola
Sebelum Anda mulai mengumpulkan data diagnostik untuk membantu akar penyebab skenario ini, pastikan Anda benar-benar melihat kebocoran memori (pertumbuhan penggunaan memori). Anda dapat menggunakan alat penghitung dotnet untuk mengonfirmasinya.
Buka jendela konsol dan navigasikan ke direktori tempat Anda mengunduh dan membuka zip target debug sampel. Jalankan target:
dotnet run
Dari konsol terpisah, temukan ID proses:
dotnet-counters ps
Outputnya harus mirip dengan:
4807 DiagnosticScena /home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios
Sekarang, periksa penggunaan memori terkelola dengan alat penghitung dotnet. --refresh-interval
menentukan jumlah detik antara refresh:
dotnet-counters monitor --refresh-interval 1 -p 4807
Output langsung harus mirip dengan:
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
Fokus pada baris ini:
GC Heap Size (MB) 4
Anda dapat melihat bahwa memori tumpukan terkelola adalah 4 MB tepat setelah startup.
Sekarang, buka URL https://localhost:5001/api/diagscenario/memleak/20000
.
Amati bahwa penggunaan memori telah tumbuh menjadi 30 MB.
GC Heap Size (MB) 30
Dengan menonton penggunaan memori, Anda dapat dengan aman mengatakan bahwa memori tumbuh atau bocor. Langkah selanjutnya adalah mengumpulkan data yang tepat untuk analisis memori.
Hasilkan cadangan memori
Saat menganalisis kemungkinan kebocoran memori, Anda memerlukan akses ke tumpuan memori aplikasi untuk menganalisis konten memori. Melihat hubungan antar objek, Anda membuat teori mengapa memori tidak dibebaskan. Sumber data diagnostik umum adalah cadangan memori di Windows atau cadangan inti yang setara di Linux. Untuk menghasilkan cadangan aplikasi .NET, Anda dapat menggunakan alat dotnet-dump .
Menggunakan target debug sampel yang sebelumnya dimulai, jalankan perintah berikut untuk menghasilkan cadangan inti Linux:
dotnet-dump collect -p 4807
Hasilnya adalah cadangan inti yang terletak di folder yang sama.
Writing minidump with heap to ./core_20190430_185145
Complete
Catatan
Untuk perbandingan dari waktu ke waktu, biarkan proses asli terus berjalan setelah mengumpulkan cadangan pertama dan mengumpulkan cadangan kedua dengan cara yang sama. Anda kemudian akan memiliki dua cadangan selama periode waktu yang dapat Anda bandingkan dengan melihat di mana penggunaan memori tumbuh.
Mulai ulang proses yang gagal
Setelah cadangan dikumpulkan, Anda harus memiliki informasi yang memadai untuk mendiagnosis proses yang gagal. Jika proses yang gagal berjalan di server produksi, sekarang ini adalah waktu yang ideal untuk remediasi jangka pendek dengan memulai ulang proses.
Dalam tutorial ini, Anda sekarang sudah selesai dengan target Debug Sampel dan Anda dapat menutupnya. Navigasi ke terminal yang memulai server, dan tekan Ctrl+C.
Menganalisis cadangan inti
Sekarang setelah Anda memiliki cadangan inti yang dihasilkan, gunakan alat dotnet-dump untuk menganalisis cadangan:
dotnet-dump analyze core_20190430_185145
Di mana core_20190430_185145
adalah nama core dump yang ingin Anda analisis.
Catatan
Jika Anda melihat kesalahan yang mengeluh bahwa libdl.so tidak dapat ditemukan, Anda mungkin harus menginstal paket libc6-dev . Untuk informasi selengkapnya, lihat Prasyarat untuk .NET di Linux.
Anda akan disajikan dengan perintah di mana Anda dapat memasukkan perintah SOS . Umumnya, hal pertama yang ingin Anda lihat adalah keadaan keseluruhan tumpukan terkelola:
> 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
Di sini Anda dapat melihat bahwa sebagian besar objek adalah objek String
atau Customer
.
Anda dapat menggunakan dumpheap
perintah lagi dengan tabel metode (MT) untuk mendapatkan daftar semua String
instans:
> 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
Anda sekarang dapat menggunakan gcroot
perintah pada System.String
instans untuk melihat bagaimana dan mengapa objek berakar:
> 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.
Anda dapat melihat bahwa String
langsung dipegang oleh Customer
objek dan secara tidak langsung dipegang oleh CustomerCache
objek.
Anda dapat terus mencadangkan objek untuk melihat bahwa sebagian besar String
objek mengikuti pola yang sama. Pada titik ini, investigasi memberikan informasi yang memadai untuk mengidentifikasi akar penyebab dalam kode Anda.
Prosedur umum ini memungkinkan Anda mengidentifikasi sumber kebocoran memori utama.
Membersihkan sumber daya
Dalam tutorial ini, Anda memulai contoh server web. Server ini seharusnya dimatikan seperti yang dijelaskan di bagian Mulai ulang proses yang gagal.
Anda juga dapat menghapus file cadangan yang dibuat.
Baca juga
- dotnet-trace untuk mencantumkan proses
- penghitung dotnet untuk memeriksa penggunaan memori terkelola
- dotnet-dump untuk mengumpulkan dan menganalisis file cadangan
- dotnet/diagnostik
- Menggunakan Visual Studio untuk men-debug kebocoran memori