Pengumpulan sampah dan performa
Artikel ini menjelaskan masalah yang terkait dengan pengumpulan sampah dan penggunaan memori. Topik ini membahas masalah yang berkaitan dengan tumpukan terkelola dan menjelaskan cara meminimalkan efek pengumpulan sampah pada aplikasi Anda. Setiap masalah memiliki tautan ke prosedur yang dapat Anda gunakan untuk menyelidiki masalah.
Alat analisis performa
Bagian berikut menjelaskan alat yang tersedia untuk menyelidiki masalah penggunaan memori dan pengumpulan sampah. Prosedur yang disediakan nanti dalam artikel ini merujuk ke alat-alat ini.
Penghitung Kinerja Memori
Anda dapat menggunakan penghitung kinerja untuk mengumpulkan data performa. Untuk petunjuknya, lihat Pembuatan Profil Runtime. Kategori memori .NET CLR dari penghitung kinerja, seperti yang dijelaskan dalam Penghitung Kinerja di .NET, memberikan informasi tentang pengumpul sampah.
Penelusuran kesalahan dengan SOS
Anda dapat menggunakan Debugger Windows (WinDbg) untuk memeriksa objek di tumpukan terkelola.
Untuk menginstal WinDbg, instal Alat penelusuran kesalahan untuk Windows dari halaman Unduh Alat Penelusuran Kesalahan untuk Windows.
Acara ETW Pengumpulan Sampah
Pelacakan peristiwa untuk Windows (ETW) adalah sistem pelacakan yang melengkapi dukungan pembuatan profil dan penelusuran kesalahan yang disediakan oleh .NET. Mulai dari .NET Framework 4, peristiwa ETW pengumpulan sampah menangkap informasi yang berguna untuk menganalisis tumpukan terkelola dari sudut pandang statistik. Misalnya, peristiwa GCStart_V1
, yang muncul saat pengumpulan sampah akan terjadi, memberikan informasi berikut:
- Pembuatan objek mana yang sedang dikumpulkan.
- Apa yang memicu pengumpulan sampah.
- Jenis pengumpulan sampah (bersamaan atau tidak bersamaan).
Pengelogan peristiwa ETW efisien dan tidak akan menutupi masalah performa apa pun yang terkait dengan pengumpulan sampah. Sebuah proses dapat menyediakan peristiwanya sendiri dalam hubungannya dengan peristiwa ETW. Saat dicatat, peristiwa aplikasi dan peristiwa pengumpulan sampah dapat dikorelasikan untuk menentukan bagaimana dan kapan masalah tumpukan terjadi. Misalnya, aplikasi server dapat menyediakan peristiwa di awal dan akhir permintaan klien.
API Pembuatan Profil
Antarmuka pembuatan profil runtime bahasa umum (CLR) memberikan informasi mendetail tentang objek yang terpengaruh selama pengumpulan sampah. Profiler dapat diberi tahu saat pengumpulan sampah dimulai dan diakhiri. Ini dapat memberikan laporan tentang objek di tumpukan terkelola, termasuk identifikasi objek di setiap generasi. Untuk informasi selengkapnya, lihat Ringkasan Pembuatan Profil.
Profiler dapat memberikan informasi yang komprehensif. Namun, profiler yang rumit berpotensi mengubah perilaku aplikasi.
Pemantauan Sumber Daya Domain Aplikasi
Mulai dari .NET Framework 4, Pemantauan sumber daya domain aplikasi (ARM) memungkinkan host memantau penggunaan CPU dan memori berdasarkan domain aplikasi. Untuk informasi selengkapnya, lihat Pemantauan Sumber Daya Domain Aplikasi.
Pecahkan masalah kinerja
Langkah pertama adalah menentukan apakah masalah sebenarnya adalah pengumpulan sampah. Jika Anda menentukannya, pilih dari daftar berikut untuk memecahkan masalah.
- Pengecualian kehabisan memori muncul
- Proses ini menggunakan terlalu banyak memori
- Pengumpul sampah tidak mengambil kembali objek dengan cukup cepat
- Tumpukan terkelola terlalu terfragmentasi
- Jeda pengumpulan sampah terlalu lama
- Generasi 0 terlalu besar
- Penggunaan CPU selama pengumpulan sampah terlalu tinggi
Masalah: Pengecualian Kehabisan Memori Muncul
Ada dua kasus yang sah untuk OutOfMemoryException terkelola agar muncul:
Kehabisan memori virtual.
Pengumpul sampah mengalokasikan memori dari sistem dalam segmen dengan ukuran yang telah ditentukan sebelumnya. Jika alokasi memerlukan segmen tambahan, tetapi tidak ada blok kosong yang tersisa di ruang memori virtual proses, alokasi untuk tumpukan terkelola akan gagal.
Tidak memiliki cukup memori fisik untuk dialokasikan.
Pemeriksaan performa |
---|
Tentukan apakah pengecualian kehabisan memori terkelola. Tentukan berapa banyak memori virtual yang dapat dicadangkan. Tentukan apakah ada cukup memori fisik. |
Jika Anda menentukan bahwa pengecualian tidak sah, hubungi Layanan Pelanggan dan Dukungan Microsoft dengan informasi berikut:
- Tumpukan dengan pengecualian kehabisan memori terkelola.
- Tempat pembuangan memori penuh.
- Data yang membuktikan bahwa itu bukan pengecualian kehabisan memori yang sah, termasuk data yang menunjukkan bahwa memori virtual atau fisik bukanlah masalah.
Masalah: Proses Menggunakan Terlalu Banyak Memori
Asumsi umumnya adalah bahwa tampilan penggunaan memori pada tab Performa Windows Task Manager dapat menunjukkan kapan terlalu banyak memori digunakan. Namun, tampilan tersebut berkaitan dengan perangkat kerja; hal ini tidak memberikan informasi tentang penggunaan memori virtual.
Jika Anda menentukan bahwa masalah disebabkan oleh tumpukan terkelola, Anda harus mengukur tumpukan terkelola dari waktu ke waktu untuk menentukan pola apa pun.
Jika Anda menentukan bahwa masalah tidak disebabkan oleh tumpukan terkelola, Anda harus menggunakan penelusuran kesalahan asli.
Masalah: Pengumpul Sampah Tidak Cukup Cepat Mengambil Objek
Ketika tampak seolah-olah objek tidak direklamasi seperti yang diharapkan untuk pengumpulan sampah, Anda harus menentukan apakah ada referensi kuat untuk objek tersebut.
Anda mungkin juga mengalami masalah ini jika tidak ada pengumpulan sampah untuk pembuatan yang berisi objek mati, yang menunjukkan bahwa finalizer untuk objek mati belum dijalankan. Misalnya, ini dimungkinkan ketika Anda menjalankan aplikasi apartemen berulir tunggal (STA) dan utas yang tidak dapat dipanggil oleh antrean finalizer layanan ke dalamnya.
Pemeriksaan performa |
---|
Periksa referensi ke objek. Tentukan apakah finalizer telah dijalankan. Tentukan apakah ada objek yang menunggu untuk diselesaikan. |
Masalah: Tumpukan Terkelola Terlalu terfragmentasi
Tingkat fragmentasi dihitung sebagai rasio ruang kosong terhadap total memori yang dialokasikan untuk generasi. Untuk generasi 2, tingkat fragmentasi yang dapat diterima tidak lebih dari 20%. Karena generasi 2 bisa menjadi sangat besar, rasio fragmentasi lebih penting daripada nilai absolut.
Memiliki banyak ruang kosong di generasi 0 tidak menjadi masalah karena ini adalah generasi di mana objek baru dialokasikan.
Fragmentasi selalu terjadi pada tumpukan objek yang besar karena tidak dipadatkan. Objek bebas yang berdekatan secara alami diciutkan menjadi satu ruang untuk memenuhi permintaan alokasi objek besar.
Fragmentasi dapat menjadi masalah pada generasi 1 dan generasi 2. Jika generasi ini memiliki banyak ruang kosong setelah pengumpulan sampah, penggunaan objek aplikasi mungkin perlu dimodifikasi, dan Anda harus mempertimbangkan untuk mengevaluasi ulang masa pakai objek jangka panjang.
Penyematan objek yang berlebihan dapat meningkatkan fragmentasi. Jika fragmentasi tinggi, terlalu banyak objek yang bisa disematkan.
Jika fragmentasi memori virtual mencegah pengumpul sampah menambahkan segmen, penyebabnya mungkin salah satu dari berikut ini:
Sering bongkar muat banyak rakitan kecil.
Menyimpan terlalu banyak referensi ke objek COM saat beroperasi dengan kode yang tidak dikelola.
Pembuatan objek sementara besar, yang menyebabkan tumpukan objek besar sering mengalokasikan dan membebaskan segmen tumpukan.
Saat menghosting CLR, aplikasi dapat meminta agar pengumpul sampah mempertahankan segmennya. Ini mengurangi frekuensi alokasi segmen. Ini dicapai dengan menggunakan bendera STARTUP_HOARD_GC_VM di Enumerasi STARTUP_FLAGS.
Pemeriksaan performa |
---|
Tentukan jumlah ruang kosong di tumpukan terkelola. Tentukan jumlah objek yang disematkan. |
Jika menurut Anda tidak ada penyebab yang sah untuk fragmentasi, hubungi Layanan dan Dukungan Pelanggan Microsoft.
Masalah: Jeda Pengumpulan Sampah Terlalu Lama
Pengumpulan sampah beroperasi secara real time lunak, sehingga aplikasi harus dapat mentolerir beberapa jeda. Kriteria untuk real time lunak adalah bahwa 95% dari operasi harus selesai tepat waktu.
Dalam pengumpulan sampah bersamaan, utas terkelola diizinkan berjalan selama pengumpulan, yang berarti jeda sangat minimal.
Pengumpulan sampah sementara (generasi 0 dan 1) hanya berlangsung beberapa milidetik, sehingga mengurangi jeda biasanya tidak mungkin dilakukan. Namun, Anda dapat mengurangi jeda di pengumpulan generasi 2 dengan mengubah pola permintaan alokasi oleh aplikasi.
Metode lain yang lebih akurat adalah menggunakan peristiwa ETW pengumpulan sampah. Anda dapat menemukan pengaturan waktu untuk koleksi dengan menambahkan perbedaan stempel waktu untuk urutan peristiwa. Seluruh urutan pengumpulan termasuk penangguhan mesin eksekusi, pengumpulan sampah itu sendiri, dan dimulainya kembali mesin eksekusi.
Anda dapat menggunakan Pemberitahuan Pengumpulan Sampah untuk menentukan apakah server akan memiliki kumpulan generasi 2, dan apakah merutekan ulang permintaan ke server lain dapat mengurangi masalah jeda.
Pemeriksaan performa |
---|
Tentukan lamanya waktu dalam pengumpulan sampah. Tentukan apa yang menyebabkan pengumpulan sampah. |
Masalah: Generasi 0 Terlalu Besar
Generasi 0 cenderung memiliki lebih banyak objek pada sistem 64-bit, terutama saat Anda menggunakan pengumpulan sampah server, bukan pengumpulan sampah stasiun kerja. Ini karena ambang batas untuk memicu pengumpulan sampah generasi 0 lebih tinggi di lingkungan ini, dan pengumpulan generasi 0 bisa menjadi jauh lebih besar. Performa ditingkatkan ketika aplikasi mengalokasikan lebih banyak memori sebelum pengumpulan sampah dipicu.
Masalah: Penggunaan CPU Selama Pengumpulan Sampah Terlalu Tinggi
Penggunaan CPU akan tinggi selama pengumpulan sampah. Jika sejumlah besar waktu proses dihabiskan dalam pengumpulan sampah, jumlah pengumpulan terlalu sering atau pengumpulan berlangsung terlalu lama. Tingkat alokasi objek yang meningkat di tumpukan terkelola menyebabkan pengumpulan sampah lebih sering terjadi. Penurunan tingkat alokasi mengurangi frekuensi pengumpulan sampah.
Anda dapat memantau tingkat alokasi dengan menggunakan penghitung kinerja Allocated Bytes/second
. Untuk informasi selengkapnya, lihat Penghitung Kinerja di .NET.
Durasi pengumpulan terutama merupakan faktor jumlah objek yang bertahan setelah alokasi. Pengumpul sampah harus melalui sejumlah besar memori jika banyak objek tetap dikumpulkan. Pekerjaan untuk mempadatkan yang selamat memakan waktu. Untuk menentukan berapa banyak objek yang ditangani selama pengumpulan, atur breakpoint di debugger di akhir pengumpulan sampah untuk generasi tertentu.
Pemeriksaan performa |
---|
Tentukan apakah penggunaan CPU yang tinggi disebabkan oleh pengumpulan sampah. Tetapkan breakpoint di akhir pengumpulan sampah. |
Panduan pemecahan masalah
Bagian ini menjelaskan pedoman yang harus Anda pertimbangkan saat memulai penyelidikan.
Pengumpulan Sampah Stasiun Kerja atau Server
Tentukan apakah Anda menggunakan jenis pengumpulan sampah yang benar. Jika aplikasi Anda menggunakan beberapa utas dan instans objek, gunakan pengumpulan sampah server, bukan pengumpulan sampah stasiun kerja. Pengumpulan sampah server beroperasi pada beberapa utas, sedangkan pengumpulan sampah stasiun kerja memerlukan beberapa instans aplikasi untuk menjalankan utas pengumpulan sampah mereka sendiri dan bersaing untuk waktu CPU.
Aplikasi yang memiliki beban rendah dan yang jarang melakukan tugas di latar belakang, seperti layanan, dapat menggunakan pengumpulan sampah stasiun kerja dengan pengumpulan sampah bersamaan dinonaktifkan.
Kapan Mengukur Ukuran Tumpukan Terkelola
Kecuali Anda menggunakan profiler, Anda harus menetapkan pola pengukuran yang konsisten untuk mendiagnosis masalah performa secara efektif. Pertimbangkan poin-poin berikut untuk membuat jadwal:
- Jika Anda mengukur setelah pengumpulan sampah generasi 2, seluruh tumpukan yang dikelola akan bebas dari sampah (objek mati).
- Jika Anda mengukur segera setelah pengumpulan sampah generasi 0, objek pada generasi 1 dan 2 belum akan dikumpulkan.
- Jika Anda mengukur segera sebelum pengumpulan sampah, Anda akan mengukur alokasi sebanyak mungkin sebelum pengumpulan sampah dimulai.
- Pengukuran selama pengumpulan sampah bermasalah, karena struktur data pengumpul sampah tidak dalam status yang valid untuk traversal dan mungkin tidak dapat memberikan Anda hasil yang lengkap. Ini memang disengaja.
- Saat menggunakan pengumpulan sampah stasiun kerja dengan pengumpulan sampah bersamaan, objek yang diambil kembali tidak dipadatkan, sehingga ukuran tumpukan bisa sama atau lebih besar (fragmentasi dapat membuatnya tampak lebih besar).
- Pengumpulan sampah bersamaan pada generasi 2 tertunda ketika beban memori fisik terlalu tinggi.
Prosedur berikut menjelaskan cara mengatur breakpoint sehingga Anda dapat mengukur tumpukan terkelola.
Untuk mengatur breakpoint di akhir pengumpulan sampah
Di WinDbg dengan ekstensi debugger SOS dimuat, masukkan perintah berikut:
bp mscorwks!WKS::GCHeap::RestartEE "j (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) 'kb';'g'"
Atur
GcCondemnedGeneration
ke generasi yang diinginkan. Perintah ini membutuhkan simbol privat.Perintah ini memaksa jeda jika
RestartEE
dijalankan setelah objek generasi 2 direklamasi untuk pengumpulan sampah.Dalam pengumpulan sampah server, hanya satu panggilan utas
RestartEE
, sehingga titik henti hanya akan terjadi sekali selama pengumpulan sampah generasi 2.
Prosedur pemeriksaan performa
Bagian ini menjelaskan prosedur berikut untuk mengisolasi penyebab masalah performa Anda:
- Tentukan apakah masalah disebabkan oleh pengumpulan sampah.
- Tentukan apakah pengecualian kehabisan memori terkelola.
- Tentukan berapa banyak memori virtual yang dapat dicadangkan.
- Tentukan apakah ada cukup memori fisik.
- Tentukan berapa banyak memori yang diterapkan oleh tumpukan terkelola.
- Tentukan berapa banyak memori yang dicadangkan tumpukan terkelola.
- Tentukan objek besar di generasi 2.
- Tentukan referensi ke objek.
- Tentukan apakah finalizer telah dijalankan.
- Tentukan apakah ada objek yang menunggu untuk diselesaikan.
- Tentukan jumlah ruang kosong di tumpukan terkelola.
- Tentukan jumlah objek yang disematkan.
- Tentukan lamanya waktu dalam pengumpulan sampah.
- Tentukan apa yang memicu pengumpulan sampah.
- Tentukan apakah penggunaan CPU yang tinggi disebabkan oleh pengumpulan sampah.
Untuk menentukan apakah masalahnya disebabkan oleh pengumpulan sampah
Periksa dua penghitung kinerja memori berikut:
% Waktu di GC. Menampilkan persentase waktu yang sudah berlalu yang dihabiskan untuk melakukan pengumpulan sampah setelah siklus pengumpulan sampah terakhir. Gunakan penghitung ini untuk menentukan apakah pengumpul sampah menghabiskan terlalu banyak waktu untuk menyediakan ruang tumpukan terkelola. Jika waktu yang dihabiskan dalam pengumpulan sampah relatif rendah, hal tersebut dapat mengindikasikan masalah sumber daya di luar tumpukan terkelola. Penghitung ini mungkin tidak akurat saat pengumpulan sampah bersamaan atau latar belakang terlibat.
# Total Byte yang diterapkan. Menampilkan jumlah memori virtual yang saat ini diterapkan oleh pengumpul sampah. Gunakan penghitung ini untuk menentukan apakah memori yang digunakan oleh pengumpul sampah adalah bagian memori yang berlebihan yang digunakan aplikasi Anda.
Sebagian besar penghitung kinerja memori diperbarui di akhir setiap pengumpulan sampah. Oleh karena itu, penghitung mungkin tidak mencerminkan kondisi saat ini yang informasinya Anda inginkan.
Untuk menentukan apakah pengecualian kehabisan memori terkelola
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah pengecualian cetak (
pe
):!pe
Jika pengecualian terkelola, OutOfMemoryException ditampilkan sebagai jenis pengecualian, seperti yang ditunjukkan dalam contoh berikut.
Exception object: 39594518 Exception type: System.OutOfMemoryException Message: <none> InnerException: <none> StackTrace (generated):
Jika output tidak menentukan pengecualian, Anda harus menentukan dari utas mana pengecualian kehabisan memori berasal. Masukkan perintah berikut di debugger untuk menampilkan semua utas dengan tumpukan panggilan mereka:
~\*kb
Utas dengan tumpukan yang memiliki panggilan pengecualian ditunjukkan oleh argumen
RaiseTheException
. Ini adalah objek pengecualian terkelola.28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
Anda dapat menggunakan perintah berikut untuk membuang pengecualian berlapis.
!pe -nested
Jika Anda tidak menemukan pengecualian apa pun, pengecualian kehabisan memori berasal dari kode yang tidak dikelola.
Untuk menentukan berapa banyak memori virtual yang dapat dicadangkan
Di WinDbg dengan ekstensi debugger SOS dimuat, masukkan perintah berikut untuk mendapatkan wilayah gratis terbesar:
!address -summary
Wilayah bebas terbesar ditampilkan seperti yang ditunjukkan pada output berikut.
Largest free region: Base 54000000 - Size 0003A980
Dalam contoh ini, ukuran wilayah bebas terbesar adalah sekitar 24000 KB (3A980 dalam heksadesimal). Wilayah ini jauh lebih kecil dari yang dibutuhkan pengumpul sampah untuk sebuah segmen.
-atau-
Gunakan perintah
vmstat
:!vmstat
Wilayah bebas terbesar adalah nilai terbesar pada kolom MAKSIMUM, seperti yang ditunjukkan pada output berikut.
TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~~ ~~~~ Free: Small 8K 64K 46K 36 1,671K Medium 80K 864K 349K 3 1,047K Large 1,384K 1,278,848K 151,834K 12 1,822,015K Summary 8K 1,278,848K 35,779K 51 1,824,735K
Untuk menentukan apakah ada cukup memori fisik
Mulai Pengelola Tugas Windows.
Pada tab
Performance
, lihat nilai yang diterapkan. (Di Windows 7, lihatCommit (KB)
diSystem group
.)Total
Jika dekat denganLimit
, Anda hampir kehabisan memori fisik.
Untuk menentukan berapa banyak memori yang diterapkan oleh tumpukan terkelola
Gunakan penghitung kinerja memori
# Total committed bytes
untuk mendapatkan jumlah byte yang diterapkan oleh tumpukan terkelola. Pengumpul sampah menerapkan potongan pada segmen yang diperlukan, tidak semua pada waktu yang sama.Catatan
Jangan gunakan penghitung kinerja
# Bytes in all Heaps
, karena tidak mewakili penggunaan memori yang sebenarnya oleh tumpukan terkelola. Ukuran generasi termasuk dalam nilai ini dan sebenarnya adalah ukuran ambangnya, yaitu ukuran yang menginduksi pengumpulan sampah jika generasi diisi dengan objek. Oleh karena itu, nilai ini biasanya nol.
Untuk menentukan berapa banyak memori cadangan tumpukan yang dikelola
Gunakan penghitung kinerja memori
# Total reserved bytes
.Pengumpul sampah menyimpan memori dalam segmen, dan Anda dapat menentukan di mana segmen dimulai dengan menggunakan
eeheap
perintah .Penting
Meskipun Anda dapat menentukan jumlah memori yang dialokasikan pengumpul sampah untuk setiap segmen, ukuran segmen bersifat khusus implementasi dan dapat berubah sewaktu-waktu, termasuk dalam pembaruan berkala. Aplikasi Anda tidak boleh membuat asumsi tentang atau bergantung pada ukuran segmen tertentu, juga tidak boleh mencoba mengonfigurasi jumlah memori yang tersedia untuk alokasi segmen.
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah berikut:
!eeheap -gc
Hasilnya adalah sebagai berikut.
Number of GC Heaps: 2 ------------------------------ Heap 0 (002db550) generation 0 starts at 0x02abe29c generation 1 starts at 0x02abdd08 generation 2 starts at 0x02ab0038 ephemeral segment allocation context: none segment begin allocated size 02ab0000 02ab0038 02aceff4 0x0001efbc(126908) Large object heap starts at 0x0aab0038 segment begin allocated size 0aab0000 0aab0038 0aab2278 0x00002240(8768) Heap Size 0x211fc(135676) ------------------------------ Heap 1 (002dc958) generation 0 starts at 0x06ab1bd8 generation 1 starts at 0x06ab1bcc generation 2 starts at 0x06ab0038 ephemeral segment allocation context: none segment begin allocated size 06ab0000 06ab0038 06ab3be4 0x00003bac(15276) Large object heap starts at 0x0cab0038 segment begin allocated size 0cab0000 0cab0038 0cab0048 0x00000010(16) Heap Size 0x3bbc(15292) ------------------------------ GC Heap Size 0x24db8(150968)
Alamat yang ditunjukkan oleh "segmen" adalah alamat awal segmen.
Untuk menentukan objek besar pada generasi 2
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah berikut:
!dumpheap –stat
Jika tumpukan terkelola besar,
dumpheap
mungkin perlu waktu cukup lama untuk menyelesaikannya.Anda dapat mulai menganalisis dari beberapa baris terakhir output, karena baris tersebut mencantumkan objek yang menggunakan ruang paling banyak. Contohnya:
2c6108d4 173712 14591808 DevExpress.XtraGrid.Views.Grid.ViewInfo.GridCellInfo 00155f80 533 15216804 Free 7a747c78 791070 15821400 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700930 19626040 System.Collections.Specialized.ListDictionary 2c64e36c 78644 20762016 DevExpress.XtraEditors.ViewInfo.TextEditViewInfo 79124228 121143 29064120 System.Object[] 035f0ee4 81626 35588936 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 791242ec 40182 90664128 System.Collections.Hashtable+bucket[] 790fa3e0 3154024 137881448 System.String Total 8454945 objects
Objek terakhir yang terdaftar adalah string dan menempati ruang paling banyak. Anda dapat memeriksa aplikasi untuk melihat cara mengoptimalkan objek string Anda. Untuk melihat string antara 150 dan 200 byte, masukkan yang berikut ini:
!dumpheap -type System.String -min 150 -max 200
Contoh hasilnya adalah sebagai berikut.
Address MT Size Gen 1875d2c0 790fa3e0 152 2 System.String HighlightNullStyle_Blotter_PendingOrder-11_Blotter_PendingOrder-11 …
Menggunakan integer, bukan string untuk ID bisa jadi lebih efisien. Jika string yang sama diulang ribuan kali, pertimbangkan interning string. Untuk informasi selengkapnya tentang interning string, lihat topik referensi untuk metode String.Intern.
Untuk menentukan referensi ke objek
Di WinDbg dengan ekstensi debugger SOS dimuat, masukkan perintah berikut untuk mencantumkan referensi ke objek:
!gcroot
-atau-
Untuk menentukan referensi objek tertentu, sertakan alamat:
!gcroot 1c37b2ac
Akar yang ditemukan di tumpukan mungkin positif palsu. Untuk informasi lebih lanjut, gunakan perintah
!help gcroot
.ebx:Root:19011c5c(System.Windows.Forms.Application+ThreadContext)-> 19010b78(DemoApp.FormDemoApp)-> 19011158(System.Windows.Forms.PropertyStore)-> … [omitted] 1c3745ec(System.Data.DataTable)-> 1c3747a8(System.Data.DataColumnCollection)-> 1c3747f8(System.Collections.Hashtable)-> 1c376590(System.Collections.Hashtable+bucket[])-> 1c376c98(System.Data.DataColumn)-> 1c37b270(System.Data.Common.DoubleStorage)-> 1c37b2ac(System.Double[]) Scan Thread 0 OSTHread 99c Scan Thread 6 OSTHread 484
Perintah
gcroot
dapat memakan waktu lama untuk diselesaikan. Objek apa pun yang tidak direklamasi oleh pengumpulan sampah adalah objek langsung. Ini berarti bahwa beberapa root secara langsung atau tidak langsung memegang objek, jadigcroot
harus mengembalikan informasi jalur ke objek. Anda harus memeriksa grafik yang dikembalikan dan melihat mengapa objek ini masih direferensikan.
Untuk menentukan apakah finalizer telah dijalankan
Jalankan program pengujian yang berisi kode berikut:
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
Jika pengujian menyelesaikan masalah, ini berarti pengumpul sampah tidak mengambil kembali objek, karena finalizer untuk objek tersebut telah ditangguhkan. Metode GC.WaitForPendingFinalizers memungkinkan finalizer menyelesaikan tugas mereka, dan memperbaiki masalah.
Untuk menentukan apakah ada objek yang menunggu untuk diselesaikan
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah berikut:
!finalizequeue
Lihatlah jumlah objek yang siap untuk finalisasi. Jika jumlahnya tinggi, Anda harus memeriksa mengapa finalizer ini tidak dapat maju sama sekali atau tidak dapat berkembang cukup cepat.
Untuk mendapatkan output utas, masukkan perintah berikut:
!threads -special
Perintah ini memberikan output seperti berikut ini.
OSID Special thread type 2 cd0 DbgHelper 3 c18 Finalizer 4 df0 GC SuspendEE
Utas finalizer menunjukkan finalizer mana, jika ada, yang sedang dijalankan. Saat utas finalizer tidak menjalankan finalizer apa pun, ia sedang menunggu peristiwa untuk memintanya melakukan tugasnya. Anda akan sering melihat utas finalizer dalam keadaan ini karena berjalan pada THREAD_HIGHEST_PRIORITY dan seharusnya menyelesaikan menjalankan finalizer, jika ada, dengan sangat cepat.
Untuk menentukan jumlah ruang kosong di tumpukan terkelola
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah berikut:
!dumpheap -type Free -stat
Perintah ini menampilkan ukuran total semua objek gratis di tumpukan terkelola, seperti yang ditunjukkan dalam contoh berikut.
total 230 objects Statistics: MT Count TotalSize Class Name 00152b18 230 40958584 Free Total 230 objects
Untuk menentukan ruang kosong di generasi 0, masukkan perintah berikut untuk informasi konsumsi memori berdasarkan pembuatan:
!eeheap -gc
Perintah ini menampilkan output yang mirip dengan berikut ini. Baris terakhir menunjukkan segmen ephemeral.
Heap 0 (0015ad08) generation 0 starts at 0x49521f8c generation 1 starts at 0x494d7f64 generation 2 starts at 0x007f0038 ephemeral segment allocation context: none segment begin allocated size 00178250 7a80d84c 7a82f1cc 0x00021980(137600) 00161918 78c50e40 78c7056c 0x0001f72c(128812) 007f0000 007f0038 047eed28 0x03ffecf0(67103984) 3a120000 3a120038 3a3e84f8 0x002c84c0(2917568) 46120000 46120038 49e05d04 0x03ce5ccc(63855820)
Hitung ruang yang digunakan oleh generasi 0:
? 49e05d04-0x49521f8c
Hasilnya adalah sebagai berikut. Generasi 0 adalah sekitar 9 MB.
Evaluate expression: 9321848 = 008e3d78
Perintah berikut membuang ruang kosong dalam rentang generasi 0:
!dumpheap -type Free -stat 0x49521f8c 49e05d04
Hasilnya adalah sebagai berikut.
------------------------------ Heap 0 total 409 objects ------------------------------ Heap 1 total 0 objects ------------------------------ Heap 2 total 0 objects ------------------------------ Heap 3 total 0 objects ------------------------------ total 409 objects Statistics: MT Count TotalSize Class Name 0015a498 409 7296540 Free Total 409 objects
Output ini menunjukkan bahwa bagian generasi 0 dari tumpukan menggunakan 9 MB ruang untuk objek dan memiliki ruang kosong 7 MB. Analisis ini menunjukkan sejauh mana generasi 0 berkontribusi terhadap fragmentasi. Jumlah penggunaan tumpukan ini harus dikurangi dari jumlah total sebagai penyebab fragmentasi oleh objek jangka panjang.
Untuk menentukan jumlah objek yang disematkan
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah berikut:
!gchandles
Statistik yang ditampilkan mencakup jumlah pegangan yang disematkan, seperti yang ditunjukkan contoh berikut.
GC Handle Statistics: Strong Handles: 29 Pinned Handles: 10
Untuk menentukan lamanya waktu dalam pengumpulan sampah
Periksa penghitung kinerja memori
% Time in GC
.Nilai tersebut dihitung dengan menggunakan interval waktu sampel. Karena penghitung diperbarui di akhir setiap pengumpulan sampah, sampel saat ini akan memiliki nilai yang sama dengan sampel sebelumnya jika tidak ada pengumpulan yang terjadi selama interval.
Waktu pengumpulan diperoleh dengan mengalikan waktu interval sampel dengan nilai persentase.
Data berikut menunjukkan empat interval pengambilan sampel dua detik, untuk studi 8 detik. Kolom
Gen0
,Gen1
, danGen2
menunjukkan jumlah total pengumpulan sampah yang telah selesai pada akhir interval untuk generasi tersebut.Interval Gen0 Gen1 Gen2 % Time in GC 1 9 3 1 10 2 10 3 1 1 3 11 3 1 3 4 11 3 1 3
Informasi ini tidak menunjukkan kapan pengumpulan sampah terjadi, tetapi Anda dapat menentukan jumlah pengumpulan sampah yang terjadi dalam selang waktu tertentu. Dengan asumsi kasus terburuk, pengumpulan sampah generasi kesepuluh 0 selesai pada awal interval kedua, dan pengumpulan sampah generasi kesebelas 0 selesai pada akhir interval ketiga. Waktu antara akhir pengumpulan sampah kesepuluh dan akhir kesebelas adalah sekitar 2 detik, dan penghitung kinerja menunjukkan 3%, sehingga durasi pengumpulan sampah generasi kesebelas 0 adalah (2 detik * 3% = 60 md).
Dalam contoh berikutnya, ada lima interval.
Interval Gen0 Gen1 Gen2 % Time in GC 1 9 3 1 3 2 10 3 1 1 3 11 4 1 1 4 11 4 1 1 5 11 4 2 20
Pengumpulan sampah generasi kedua 2 dimulai selama interval keempat dan selesai pada interval kelima. Dengan asumsi kasus terburuk, pengumpulan sampah terakhir adalah untuk koleksi generasi 0 yang selesai pada awal interval ketiga, dan pengumpulan sampah generasi 2 selesai pada akhir interval kelima. Oleh karena itu, waktu antara akhir pengumpulan sampah generasi 0 dan akhir pengumpulan sampah generasi 2 adalah 4 detik. Karena penghitung
% Time in GC
adalah 20%, jumlah waktu maksimum yang dapat diambil oleh pengumpulan sampah generasi 2 adalah (4 detik * 20% = 800 md).Atau, Anda dapat menentukan panjang pengumpulan sampah dengan menggunakan peristiwa ETW pengumpulan sampah, dan menganalisis informasi untuk menentukan durasi pengumpulan sampah.
Misalnya, data berikut menunjukkan urutan peristiwa yang terjadi selama pengumpulan sampah yang tidak bersamaan.
Timestamp Event name 513052 GCSuspendEEBegin_V1 513078 GCSuspendEEEnd 513090 GCStart_V1 517890 GCEnd_V1 517894 GCHeapStats 517897 GCRestartEEBegin 517918 GCRestartEEEnd
Menangguhkan utas yang dikelola membutuhkan waktu 26us (
GCSuspendEEEnd
–GCSuspendEEBegin_V1
).Pengumpulan sampah sebenarnya membutuhkan waktu 4,8 md (
GCEnd_V1
–GCStart_V1
).Melanjutkan utas terkelola membutuhkan waktu 21us (
GCRestartEEEnd
–GCRestartEEBegin
).Output berikut memberikan contoh untuk pengumpulan sampah latar belakang, dan menyertakan bidang proses, utas, dan peristiwa. (Tidak semua data ditampilkan.)
timestamp(us) event name process thread event field 42504385 GCSuspendEEBegin_V1 Test.exe 4372 1 42504648 GCSuspendEEEnd Test.exe 4372 42504816 GCStart_V1 Test.exe 4372 102019 42504907 GCStart_V1 Test.exe 4372 102020 42514170 GCEnd_V1 Test.exe 4372 42514204 GCHeapStats Test.exe 4372 102020 42832052 GCRestartEEBegin Test.exe 4372 42832136 GCRestartEEEnd Test.exe 4372 63685394 GCSuspendEEBegin_V1 Test.exe 4744 6 63686347 GCSuspendEEEnd Test.exe 4744 63784294 GCRestartEEBegin Test.exe 4744 63784407 GCRestartEEEnd Test.exe 4744 89931423 GCEnd_V1 Test.exe 4372 102019 89931464 GCHeapStats Test.exe 4372
Peristiwa
GCStart_V1
di 42504816 menunjukkan bahwa ini adalah pengumpulan sampah latar belakang, karena bidang terakhir adalah1
. Ini menjadi tempat pengumpulan sampah No. 102019.Peristiwa
GCStart
terjadi karena ada kebutuhan untuk pengumpulan sampah sementara sebelum Anda memulai pengumpulan sampah latar belakang. Ini menjadi tempat pengumpulan sampah No. 102020.Pada 42514170, pengumpulan sampah No. 102020 selesai. Utas yang dikelola dimulai ulang pada saat ini. Ini selesai pada utas 4372, yang memicu pengumpulan sampah latar belakang ini.
Pada utas 4744, penangguhan terjadi. Ini adalah satu-satunya waktu di mana pengumpulan sampah latar belakang harus menangguhkan utas terkelola. Durasi ini kira-kira 99md ((63784407-63685394)/1000).
Peristiwa
GCEnd
untuk pengumpulan sampah latar belakang ada di 89931423. Ini berarti bahwa pengumpulan sampah latar belakang berlangsung selama sekitar 47detik ((89931423-42504816)/1000).Saat utas terkelola berjalan, Anda dapat melihat sejumlah pengumpulan sampah sementara yang terjadi.
Untuk menentukan apa yang memicu pengumpulan sampah
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS yang dimuat, masukkan perintah berikut untuk menampilkan semua utas dengan tumpukan panggilan mereka:
~*kb
Perintah ini menampilkan output yang mirip dengan berikut ini.
0012f3b0 79ff0bf8 mscorwks!WKS::GCHeap::GarbageCollect 0012f454 30002894 mscorwks!GCInterface::CollectGeneration+0xa4 0012f490 79fa22bd fragment_ni!request.Main(System.String[])+0x48
Jika pengumpulan sampah disebabkan oleh pemberitahuan memori rendah dari sistem operasi, tumpukan panggilan serupa, kecuali bahwa utas adalah utas finalizer. Utas finalizer mendapat pemberitahuan memori rendah asinkron dan menginduksi pengumpulan sampah.
Jika pengumpulan sampah disebabkan oleh alokasi memori, tumpukan akan muncul sebagai berikut:
0012f230 7a07c551 mscorwks!WKS::GCHeap::GarbageCollectGeneration 0012f2b8 7a07cba8 mscorwks!WKS::gc_heap::try_allocate_more_space+0x1a1 0012f2d4 7a07cefb mscorwks!WKS::gc_heap::allocate_more_space+0x18 0012f2f4 7a02a51b mscorwks!WKS::GCHeap::Alloc+0x4b 0012f310 7a02ae4c mscorwks!Alloc+0x60 0012f364 7a030e46 mscorwks!FastAllocatePrimitiveArray+0xbd 0012f424 300027f4 mscorwks!JIT_NewArr1+0x148 000af70f 3000299f fragment_ni!request..ctor(Int32, Single)+0x20c 0000002a 79fa22bd fragment_ni!request.Main(System.String[])+0x153
Pembantu just-in-time (
JIT_New*
) akhirnya memanggilGCHeap::GarbageCollectGeneration
. Jika Anda menentukan bahwa pengumpulan sampah generasi 2 disebabkan oleh alokasi, Anda harus menentukan objek mana yang dikumpulkan oleh kumpulan sampah generasi 2 dan bagaimana cara menghindarinya. Artinya, Anda ingin menentukan perbedaan antara awal dan akhir pengumpulan sampah generasi 2, dan objek yang menyebabkan pengumpulan generasi 2.Misalnya, masukkan perintah berikut di debugger untuk memperlihatkan awal koleksi generasi 2:
!dumpheap –stat
Contoh output (diringkas untuk menunjukkan objek yang paling banyak menggunakan ruang):
79124228 31857 9862328 System.Object[] 035f0384 25668 11601936 Toolkit.TlkPosition 00155f80 21248 12256296 Free 79103b6c 297003 13068132 System.Threading.ReaderWriterLock 7a747ad4 708732 14174640 System.Collections.Specialized.HybridDictionary 7a747c78 786498 15729960 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700298 19608344 System.Collections.Specialized.ListDictionary 035f0ee4 89192 38887712 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 7912c444 91616 71887080 System.Double[] 791242ec 32451 82462728 System.Collections.Hashtable+bucket[] 790fa3e0 2459154 112128436 System.String Total 6471774 objects
Ulangi perintah di akhir generasi 2:
!dumpheap –stat
Contoh output (diringkas untuk menunjukkan objek yang paling banyak menggunakan ruang):
79124228 26648 9314256 System.Object[] 035f0384 25668 11601936 Toolkit.TlkPosition 79103b6c 296770 13057880 System.Threading.ReaderWriterLock 7a747ad4 708730 14174600 System.Collections.Specialized.HybridDictionary 7a747c78 786497 15729940 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700298 19608344 System.Collections.Specialized.ListDictionary 00155f80 13806 34007212 Free 035f0ee4 89187 38885532 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 791242ec 32370 82359768 System.Collections.Hashtable+bucket[] 790fa3e0 2440020 111341808 System.String Total 6417525 objects
Objek
double[]
menghilang dari akhir output, yang berarti bahwa objek tersebut dikumpulkan. Objek-objek ini berjumlah sekitar 70 MB. Objek yang tersisa tidak banyak berubah. Oleh karena itu, objekdouble[]
ini adalah alasan mengapa pengumpulan sampah generasi 2 ini terjadi. Langkah Anda selanjutnya adalah menentukan mengapa objekdouble[]
ada di sana dan mengapa mereka mati. Anda dapat meminta pengembang kode dari mana objek ini berasal, atau Anda dapat menggunakangcroot
perintah .
Untuk menentukan apakah penggunaan CPU yang tinggi disebabkan oleh pengumpulan sampah
Hubungkan nilai penghitung kinerja memori
% Time in GC
dengan waktu proses.Jika nilai
% Time in GC
melonjak bersamaan dengan waktu proses, pengumpulan sampah menyebabkan penggunaan CPU yang tinggi. Jika tidak, buat profil aplikasi untuk menemukan di mana penggunaan tinggi terjadi.