Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Artikel ini menjelaskan masalah yang terkait dengan pengumpulan sampah dan penggunaan memori. Ini mengatasi 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 kinerja
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 Performa Memori
Anda dapat menggunakan penghitung kinerja untuk mengumpulkan data performa. Untuk petunjuknya, lihat Pembuatan Profil Runtime. Kategori Memori .NET CLR penghitung kinerja, seperti yang dijelaskan dalam Penghitung Kinerja di .NET, memberikan informasi tentang pengumpul sampah.
Pemecahan masalah dengan SOS
Anda dapat menggunakan Windows Debugger (WinDbg) untuk memeriksa objek pada tumpukan terkelola.
Untuk menginstal WinDbg, instal Alat Penelusuran Kesalahan untuk Windows dari halaman Unduh Alat Penelusuran Kesalahan untuk Windows .
Peristiwa ETW Pengumpulan Sampah
Pelacakan peristiwa untuk Windows (ETW) adalah sistem pelacakan yang melengkapi dukungan pembuatan profil dan penelusuran kesalahan yang disediakan oleh .NET. Dimulai dengan .NET Framework 4, peristiwa pengumpulan sampah ETW menangkap informasi yang berguna untuk menganalisis heap terkelola dari sudut pandang statistik. Misalnya, peristiwa GCStart_V1 yang muncul saat pengumpulan sampah akan segera terjadi memberikan informasi berikut:
- Generasi objek mana yang sedang dikumpulkan.
- Apa yang memicu pengumpulan sampah.
- Jenis pengumpulan sampah (bersamaan atau tidak bersamaan).
Pencatatan peristiwa ETW efisien serta tidak akan menutupi masalah performa apa pun yang terkait dengan pengumpulan sampah. Proses dapat menghasilkan peristiwa sendiri bersama dengan peristiwa ETW. Ketika dicatat, peristiwa aplikasi dan peristiwa pengumpulan sampah dapat berkorelasi untuk menentukan bagaimana dan kapan masalah timbunan 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 terperinci tentang objek yang terpengaruh selama pengumpulan sampah. Profiler dapat diberi tahu ketika pengumpulan sampah dimulai dan berakhir. Ini dapat memberikan laporan tentang objek pada tumpukan terkelola, termasuk identifikasi objek di setiap generasi. Untuk informasi selengkapnya, lihat Gambaran Umum Profil.
Profiler dapat memberikan informasi yang komprehensif. Namun, profiler kompleks berpotensi memodifikasi perilaku aplikasi.
Pemantauan Sumber Daya Domain Aplikasi
Dimulai dengan .NET Framework 4, Pemantauan sumber daya domain aplikasi (ARM) memungkinkan host untuk memantau penggunaan CPU dan memori berdasarkan domain aplikasi. Untuk informasi selengkapnya, lihat Pemantauan Sumber Daya Domain Aplikasi.
Memecahkan masalah performa
Langkah pertama adalah menentukan apakah masalahnya sebenarnya adalah pengumpulan sampah. Jika Anda menentukan bahwa masalahnya adalah ini, pilih dari daftar berikut untuk memecahkan masalah.
- Pengecualian karena kehabisan memori terjadi
- Proses ini menggunakan terlalu banyak memori
- Pengumpul sampah tidak mengklaim kembali objek dengan cukup cepat
- Tumpukan terkelola terlalu terfragmentasi
- Jeda pengumpulan sampah terlalu panjang
- Generasi 0 terlalu besar
- Penggunaan CPU selama pengumpulan sampah terlalu tinggi
Masalah: Pengecualian Di Luar Memori Dilemparkan
Ada dua kasus sah di mana sebuah OutOfMemoryException yang dikelola bisa dilemparkan:
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 bebas kontigu 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 tersebut dikelola. Tentukan berapa banyak memori virtual yang dapat dialokasikan. Tentukan apakah ada cukup memori fisik. |
Jika Anda menentukan bahwa pengecualian tersebut tidak sah, hubungi Layanan Pelanggan dan Dukungan Microsoft dengan informasi berikut:
- Tumpukan dengan pengecualian kehabisan memori terkelola.
- Pembuangan memori penuh.
- Data yang membuktikan bahwa itu bukan pengecualian kehabisan memori yang sah, termasuk data yang menunjukkan bahwa masalahnya bukan pada memori virtual atau fisik.
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 yang digunakan. Namun, tampilan tersebut berkaitan dengan set kerja; 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 tersebut tidak disebabkan oleh timbunan terkelola, Anda harus menggunakan penelusuran kesalahan asli.
Masalah: Pengumpul Sampah Tidak Mengklaim Ulang Objek Dengan Cukup Cepat
Ketika tampak seolah-olah objek tidak diambil kembali seperti yang diharapkan untuk proses pengumpulan sampah, Anda harus memastikan apakah ada referensi kuat ke objek tersebut.
Anda mungkin juga mengalami masalah ini jika belum ada pengumpulan sampah untuk generasi yang berisi objek mati, yang menunjukkan bahwa finalizer untuk objek mati belum dijalankan. Misalnya, ini dimungkinkan ketika Anda menjalankan aplikasi single-threaded apartment (STA) dan utas yang melayani antrian finalizer tidak dapat memanggil atau mengaksesnya.
| 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 yang dibagi dengan 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 di tumpukan objek besar karena tidak dikompresi. Objek bebas yang berdekatan secara alami diciutkan ke dalam satu ruang untuk memenuhi permintaan alokasi objek besar.
Fragmentasi dapat menjadi masalah pada generasi 1 dan generasi 2. Jika generasi ini memiliki sejumlah besar ruang kosong setelah pengumpulan sampah, penggunaan objek aplikasi mungkin perlu dimodifikasi, dan Anda harus mempertimbangkan untuk mengevaluasi kembali masa pakai objek jangka panjang.
Terlalu banyak menyematkan objek dapat meningkatkan fragmentasi. Jika fragmentasi tinggi, terlalu banyak objek mungkin telah disematkan.
Jika fragmentasi memori virtual mencegah pengumpul sampah menambahkan segmen, penyebabnya bisa menjadi salah satu hal berikut:
Sering memuat dan membongkar banyak rakitan kecil.
Menyimpan terlalu banyak referensi ke objek COM saat berinteraksi dengan kode yang tidak dikelola.
Pembuatan objek sementara besar, yang menyebabkan tumpukan objek besar sering mengalokasikan dan membebaskan segmen timbunan.
Saat menghosting CLR, aplikasi dapat meminta agar pengumpul sampah mempertahankan segmennya. Ini mengurangi frekuensi alokasi segmen. Ini dicapai dengan menggunakan flag STARTUP_HOARD_GC_VM di Enumerasi STARTUP_FLAGS.
| Pemeriksaan performa |
|---|
|
Tentukan jumlah ruang kosong dalam tumpukan terkelola. Tentukan jumlah objek yang disematkan. |
Jika Anda berpikir bahwa tidak ada penyebab yang sah untuk fragmentasi, hubungi Layanan dan Dukungan Pelanggan Microsoft.
Masalah: Jeda Pengumpulan Sampah Terlalu Panjang
Pengumpulan sampah beroperasi secara real time, sehingga aplikasi harus dapat mentolerir beberapa jeda. Kriteria untuk real time lunak adalah bahwa 95% operasi harus selesai tepat waktu.
Dalam pengumpulan sampah secara bersamaan, utas terkelola diizinkan untuk berjalan selama pengumpulan, yang berarti jeda sangat minimal.
Pengumpulan sampah sementara (generasi 0 dan 1) hanya berlangsung beberapa milidetik, sehingga mengurangi jeda biasanya tidak layak. Namun, Anda dapat mengurangi jeda dalam koleksi generasi 2 dengan mengubah pola permintaan alokasi oleh aplikasi.
Metode lain yang lebih akurat adalah menggunakan peristiwa ETW pengumpulan sampah. Anda dapat menemukan waktu untuk koleksi dengan menambahkan perbedaan stempel waktu untuk urutan peristiwa. Seluruh rangkaian pengumpulan mencakup penangguhan mesin eksekusi, pengumpulan sampah itu sendiri, dan pengaktifan kembali mesin eksekusi.
Anda dapat menggunakan Pemberitahuan Pengumpulan Sampah untuk menentukan apakah server akan melakukan pengumpulan generasi 2, dan apakah pengalihan permintaan ke server lain dapat mengurangi masalah dengan jeda.
| Pemeriksaan performa |
|---|
|
Tentukan lamanya waktu dalam pengumpulan sampah. Tentukan apa yang menyebabkan pengumpulan sampah. |
Masalah: Generasi 0 Terlalu Besar
Generasi 0 kemungkinan memiliki jumlah objek yang lebih besar pada sistem 64-bit, terutama ketika Anda menggunakan pengumpulan sampah server alih-alih pengumpulan sampah stasiun kerja. Ini karena ambang untuk memicu pengumpulan sampah generasi 0 lebih tinggi di lingkungan ini, dan koleksi 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, frekuensi pengumpulan terlalu sering atau waktu pengumpulan terlalu lama. Peningkatan tingkat alokasi objek pada tumpukan terkelola menyebabkan pengumpulan sampah lebih sering terjadi. Mengurangi tingkat alokasi mengurangi frekuensi pengumpulan sampah.
Anda dapat memantau tingkat alokasi dengan menggunakan penghitung performa Allocated Bytes/second. Untuk informasi selengkapnya, lihat Penghitung Kinerja di .NET.
Durasi koleksi terutama merupakan faktor jumlah objek yang bertahan setelah alokasi. Pengumpul sampah harus melalui sejumlah memori yang besar jika banyak objek masih harus dikumpulkan. Pekerjaan untuk memampatkan para penyintas memakan waktu. Untuk menentukan berapa banyak objek yang ditangani selama koleksi, atur titik henti di debugger di akhir pengumpulan sampah untuk generasi tertentu.
| Pemeriksaan performa |
|---|
|
Tentukan apakah penggunaan CPU tinggi disebabkan oleh pengumpulan sampah. Atur titik henti di akhir pengumpulan sampah. |
Panduan pemecahan masalah
Bagian ini menjelaskan panduan yang harus Anda pertimbangkan saat memulai penyelidikan.
Pengumpulan Sampah Workstation atau Server
Tentukan apakah Anda menggunakan jenis pengumpulan sampah yang benar. Jika aplikasi Anda menggunakan beberapa utas dan instans objek, gunakan pengumpulan sampah server alih-alih pengumpulan sampah stasiun kerja. Pengumpulan sampah pada server beroperasi pada beberapa utas, sedangkan pengumpulan sampah pada stasiun kerja memerlukan beberapa instans aplikasi untuk menjalankan utas pengumpulan sampah mereka sendiri dan bersaing untuk waktu pemrosesan 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 Timbunan Terkelola
Kecuali Anda menggunakan profiler, Anda harus membuat pola pengukuran yang konsisten untuk mendiagnosis masalah performa secara efektif. Pertimbangkan poin-poin berikut untuk menetapkan jadwal:
- Jika Anda mengukur setelah pengumpulan sampah generasi 2, seluruh tumpukan yang dikelola akan bebas dari sampah (benda mati).
- Jika Anda mengukur segera setelah pengumpulan sampah generasi 0, objek dalam generasi 1 dan 2 belum akan dikumpulkan.
- Jika Anda mengukur segera sebelum pengumpulan sampah, Anda akan mengukur alokasi sebanyak mungkin sebelum pengumpulan sampah dimulai.
- Mengukur pada saat pengumpulan sampah memiliki masalah, karena struktur data pengumpul sampah tidak berada dalam kondisi valid untuk penelusuran dan mungkin tidak dapat memberikan hasil yang lengkap. Ini memang disengaja.
- Ketika Anda menggunakan pengumpulan sampah stasiun kerja dengan pengumpulan sampah bersamaan, objek yang direklamasi tidak dikompresi, 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 titik henti sehingga Anda dapat mengukur tumpukan terkelola.
Untuk mengatur titik henti 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
GcCondemnedGenerationke generasi yang diinginkan. Perintah ini memerlukan simbol privat.Perintah ini memaksa penghentian jika
RestartEEdijalankan setelah objek generasi 2 telah dibersihkan dalam proses pengumpulan sampah.Dalam pengumpulan sampah server, hanya satu utas yang memanggil
RestartEE, sehingga titik henti hanya akan terjadi sekali selama pengumpulan sampah generasi 2.
Prosedur pemeriksaan kinerja
Bagian ini menjelaskan prosedur berikut untuk mengisolasi penyebab masalah performa Anda:
- Tentukan apakah masalah disebabkan oleh pengumpulan sampah.
- Tentukan apakah pengecualian kehabisan memori tersebut dikelola.
- Tentukan berapa banyak memori virtual yang dapat dialokasikan.
- Tentukan apakah ada cukup memori fisik.
- Tentukan berapa banyak memori yang dialokasikan oleh heap terkelola.
- Tentukan berapa banyak memori yang dicadangkan oleh 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 dalam tumpukan terkelola.
- Tentukan jumlah objek yang disematkan.
- Tentukan lamanya waktu dalam pengumpulan sampah.
- Tentukan apa yang memicu pengumpulan sampah.
- Tentukan apakah penggunaan CPU tinggi disebabkan oleh pengumpulan sampah.
Untuk menentukan apakah masalah disebabkan oleh pengumpulan sampah
Periksa dua penghitung kinerja memori berikut:
% Waktu dalam GC. Menampilkan persentase waktu yang 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 membuat ruang timbunan terkelola tersedia. Jika waktu yang dihabiskan dalam pengumpulan sampah relatif rendah, itu dapat menunjukkan masalah sumber daya di luar tumpukan terkelola. Penghitung ini mungkin tidak akurat ketika pengumpulan sampah bersamaan atau pengumpulan sampah latar belakang sedang berlangsung.
# Total Byte yang dialokasikan. Menampilkan jumlah memori virtual yang saat ini dilakukan oleh pengumpul sampah. Gunakan penghitung ini untuk menentukan apakah memori yang dikonsumsi 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, mereka mungkin tidak mencerminkan kondisi saat ini yang Anda inginkan informasinya.
Untuk menentukan apakah pengecualian kehabisan memori ditangani
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah cetak pengecualian (
pe):!peJika pengecualian dikelola, 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 menyebutkan pengecualian, Anda harus menentukan utas mana yang menyebabkan pengecualian kehabisan memori. Masukkan perintah berikut di debugger untuk menampilkan semua utas dengan tumpukan panggilan mereka:
~\*kbUtas yang memiliki tumpukan dengan panggilan pengecualian ditunjukkan oleh argumen
RaiseTheException. Ini adalah objek pengecualian terkelola.28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0Anda dapat menggunakan perintah berikut untuk mencadangkan pengecualian berlapis.
!pe -nestedJika Anda tidak menemukan pengecualian apa pun, pengecualian kehabisan memori berasal dari kode yang tidak dikelola.
Untuk menentukan berapa banyak memori virtual yang dapat dipesan
Di WinDbg dengan ekstensi debugger SOS dimuat, masukkan perintah berikut untuk mendapatkan wilayah gratis terbesar:
!address -summaryWilayah gratis terbesar ditampilkan seperti yang ditunjukkan dalam output berikut.
Largest free region: Base 54000000 - Size 0003A980Dalam contoh ini, ukuran wilayah bebas terbesar adalah sekitar 24000 KB (3A980 dalam heksadesimal). Wilayah ini jauh lebih kecil dari yang dibutuhkan pengumpul sampah untuk suatu segmen.
-atau-
Gunakan perintah
vmstat:!vmstatWilayah bebas terbesar adalah nilai terbesar di 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 Windows Task Manager.
Pada tab
Performance, lihat nilai yang diterapkan. (Di Windows 7, lihatCommit (KB)diSystem group.)Jika
Totaldekat denganLimit, Anda hampir kehabisan memori fisik.
Untuk menentukan berapa banyak memori yang dialokasikan heap terkelola
Gunakan penghitung kinerja memori
# Total committed bytesuntuk mendapatkan jumlah byte yang dicadangkan oleh heap terkelola. Pengumpul sampah mengalokasikan bagian di satu segmen sesuai kebutuhan, bukan secara bersamaan.Nota
Jangan gunakan penghitung
# Bytes in all Heapsperforma, karena tidak mewakili penggunaan memori yang sebenarnya oleh heap terkelola. Ukuran generasi termasuk dalam nilai ini dan sebenarnya ukuran ambang batasnya, 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 terkelola
Gunakan penghitung
# Total reserved bytesperforma memori.Pengumpul sampah menyimpan memori dalam segmen, dan Anda dapat menentukan di mana segmen dimulai dengan menggunakan
eeheapperintah .Penting
Meskipun Anda dapat menentukan jumlah memori yang dialokasikan pengumpul sampah untuk setiap segmen, ukuran segmen bersifat spesifik implementasi dan dapat berubah kapan saja, 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 -gcHasilnya 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-objek besar di generasi kedua
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah berikut:
!dumpheap –statJika tumpukan terkelola besar,
dumpheapmungkin perlu waktu cukup lama untuk menyelesaikannya.Anda dapat mulai menganalisis dari beberapa baris terakhir output, karena mereka mencantumkan objek yang menggunakan ruang terbanyak. 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 objectsObjek terakhir yang tercantum adalah string dan menempati ruang terbanyak. Anda dapat memeriksa aplikasi untuk melihat bagaimana objek string Anda dapat dioptimalkan. Untuk melihat string antara 150 dan 200 byte, masukkan yang berikut ini:
!dumpheap -type System.String -min 150 -max 200Contoh hasilnya adalah sebagai berikut.
Address MT Size Gen 1875d2c0 790fa3e0 152 2 System.String HighlightNullStyle_Blotter_PendingOrder-11_Blotter_PendingOrder-11 …Menggunakan bilangan bulat alih-alih string untuk ID bisa lebih efisien. Jika sebuah string yang sama diulang ribuan kali, pertimbangkan penggunaan string interning. 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 untuk objek tertentu, sertakan alamat:
!gcroot 1c37b2acAkar yang ditemukan pada 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 484Perintah
gcrootdapat memakan waktu lama untuk diselesaikan. Objek apa pun yang tidak direklamasi oleh pengumpulan sampah otomatis adalah objek yang masih hidup. Ini berarti bahwa beberapa root secara langsung atau tidak langsung memegang objek, jadigcrootharus mengembalikan informasi jalur ke objek. Anda harus memeriksa grafik yang dikembalikan dan melihat mengapa objek ini masih direferensikan.
Untuk menentukan apakah fungsi finalisasi telah dijalankan
Jalankan program pengujian yang berisi kode berikut:
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();Jika pengujian menyelesaikan masalah, ini berarti bahwa pengumpul sampah tidak mengklaim 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:
!finalizequeueLihatlah jumlah objek yang siap untuk finalisasi. Jika angkanya tinggi, Anda harus memeriksa mengapa finalizer ini tidak dapat maju sama sekali atau tidak dapat maju cukup cepat.
Untuk mendapatkan output utas, masukkan perintah berikut:
!threads -specialPerintah ini menyediakan output seperti berikut ini.
OSID Special thread type 2 cd0 DbgHelper 3 c18 Finalizer 4 df0 GC SuspendEEUtas finalizer menunjukkan finalizer mana, jika ada, yang saat ini sedang dijalankan. Ketika thread finalizer tidak menjalankan finalizer apa pun, thread tersebut sedang menunggu sebuah peristiwa yang menyuruhnya untuk melakukan pekerjaannya. Sebagian besar waktu Anda akan melihat utas finalizer dalam status ini karena berjalan pada THREAD_HIGHEST_PRIORITY dan seharusnya selesai menjalankan finalizer, jika ada, sangat cepat.
Untuk menentukan jumlah ruang kosong dalam tumpukan terkelola
Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah berikut:
!dumpheap -type Free -statPerintah ini menampilkan ukuran total semua objek bebas pada tumpukan terkelola, seperti yang ditunjukkan dalam contoh berikut.
total 230 objects Statistics: MT Count TotalSize Class Name 00152b18 230 40958584 Free Total 230 objectsUntuk menentukan ruang kosong di generasi 0, masukkan perintah berikut untuk mendapatkan informasi konsumsi memori berdasarkan generasi:
!eeheap -gcPerintah ini menampilkan output yang mirip dengan yang berikut ini. Baris terakhir menunjukkan segmen sementara.
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-0x49521f8cHasilnya adalah sebagai berikut. Generasi 0 sekitar 9 MB.
Evaluate expression: 9321848 = 008e3d78Perintah berikut mencadangkan ruang kosong dalam rentang generasi 0:
!dumpheap -type Free -stat 0x49521f8c 49e05d04Hasilnya 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 objectsOutput ini menunjukkan bahwa segmen generasi 0 dari timbunan menggunakan 9 MB ruang untuk objek dan memiliki 7 MB ruang bebas. Analisis ini menunjukkan sejauh mana generasi 0 berkontribusi pada fragmentasi. Jumlah penggunaan timbunan ini harus didiskon 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:
!gchandlesStatistik yang ditampilkan mencakup jumlah pegangan yang disematkan, seperti yang ditunjukkan oleh contoh berikut.
GC Handle Statistics: Strong Handles: 29 Pinned Handles: 10
Untuk menentukan lamanya waktu dalam pengumpulan sampah
Periksa penghitung
% Time in GCkinerja memori.Nilai dihitung dengan menggunakan waktu interval sampel. Karena penghitung diperbarui di akhir setiap pengumpulan sampah, sampel saat ini akan memiliki nilai yang sama dengan sampel sebelumnya jika tidak ada koleksi yang terjadi selama interval.
Waktu pengumpulan diperoleh dengan mengalikan waktu interval sampel dengan nilai persentase.
Untuk studi 8 detik, data berikut menunjukkan empat interval waktu pengambilan sampel, masing-masing dua detik. Kolom
Gen0,Gen1, danGen2menunjukkan 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 3Informasi ini tidak menunjukkan kapan pengumpulan sampah terjadi, tetapi Anda dapat menentukan jumlah pengumpulan sampah yang terjadi dalam selang waktu. 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 kesepuluh dan akhir pengumpulan sampah kesebelas adalah sekitar 2 detik, dan penghitung kinerja menunjukkan 3%, sehingga durasi pengumpulan sampah generasi kesebelas 0 adalah (2 detik * 3% = 60ms).
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 20Pengumpulan 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 GCadalah 20%, jumlah maksimum waktu yang bisa diambil pengumpulan sampah generasi 2 adalah (4 detik * 20% = 800ms).Atau, Anda dapat menentukan lamanya 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 non-bersamaan.
Timestamp Event name 513052 GCSuspendEEBegin_V1 513078 GCSuspendEEEnd 513090 GCStart_V1 517890 GCEnd_V1 517894 GCHeapStats 517897 GCRestartEEBegin 517918 GCRestartEEEndMenangguhkan utas terkelola membutuhkan waktu 26us (
GCSuspendEEEnd–GCSuspendEEBegin_V1).Pengumpulan sampah aktual membutuhkan waktu 4,8ms (
GCEnd_V1–GCStart_V1).Melanjutkan utas yang terkelola membutuhkan waktu 21us (
GCRestartEEEnd–GCRestartEEBegin).Keluaran berikut memberikan contoh dari pengumpulan sampah latar belakang dan mencakup komponen 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 4372Peristiwa
GCStart_V1di 42504816 menunjukkan bahwa ini adalah pengumpulan sampah latar belakang, karena bidang terakhir adalah1. Ini adalah pengumpulan sampah No. 102019.Peristiwa ini
GCStartterjadi karena ada kebutuhan akan pengumpulan sampah sesehemeral sebelum Anda memulai pengumpulan sampah latar belakang. Ini menjadi pengumpulan sampah No. 102020.Pada titik 42514170, pengumpulan sampah No. 102020 selesai. Utas terkelola dijalankan ulang pada saat ini. Pengumpulan sampah latar belakang ini dipicu dan diselesaikan pada utas 4372.
Terjadi penangguhan pada utas 4744. Ini adalah satu-satunya waktu di mana pengumpulan sampah latar belakang harus menghentikan sementara utas terkelola. Durasi ini sekitar 99ms ((63784407-63685394)/1000).
Acara
GCEnduntuk pengumpulan sampah latar belakang terjadi pada 89931423. Ini berarti bahwa pengumpulan sampah latar belakang berlangsung selama sekitar 47 detik ((89931423-42504816)/1000).Saat thread yang terkelola berjalan, Anda dapat melihat sejumlah pengumpulan sampah ephemeris 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 yang berikut ini.
0012f3b0 79ff0bf8 mscorwks!WKS::GCHeap::GarbageCollect 0012f454 30002894 mscorwks!GCInterface::CollectGeneration+0xa4 0012f490 79fa22bd fragment_ni!request.Main(System.String[])+0x48Jika pengumpulan sampah disebabkan oleh pemberitahuan memori rendah dari sistem operasi, stack panggilan akan serupa, kecuali bahwa utas yang digunakan adalah utas finalizer. Thread finalizer menerima pemberitahuan memori rendah secara asinkron dan memicu pengumpulan sampah.
Jika pengumpulan sampah disebabkan oleh alokasi memori, tumpukan 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[])+0x153Pembantu 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 pengumpulan sampah generasi 2 dan cara menghindarinya. Artinya, Anda ingin menentukan perbedaan antara awal dan akhir pengumpulan sampah generasi 2, dan objek yang menyebabkan koleksi generasi 2.Misalnya, masukkan perintah berikut di debugger untuk memperlihatkan awal koleksi generasi 2:
!dumpheap –statContoh output (dipersingkat untuk menampilkan objek yang menggunakan ruang terbanyak):
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 objectsUlangi perintah di akhir generasi 2:
!dumpheap –statContoh output (dipersingkat untuk menampilkan objek yang menggunakan ruang terbanyak):
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 objectsObjek
double[]menghilang dari akhir output, yang berarti bahwa mereka dikumpulkan. Objek-objek ini mencakup sekitar 70 MB. Objek yang tersisa tidak banyak berubah. Oleh karena itu, objek-objek inidouble[]adalah alasan mengapa pengumpulan sampah generasi 2 ini terjadi. Langkah Anda selanjutnya adalah menentukan mengapadouble[]objek ada di sana dan mengapa mereka meninggal. Anda dapat meminta pengembang kode dari mana objek ini berasal, atau Anda dapat menggunakangcrootperintah .
Untuk menentukan apakah penggunaan CPU tinggi disebabkan oleh pengumpulan sampah
Menghubungkan nilai penghitung
% Time in GCkinerja memori dengan waktu proses.% Time in GCJika nilai melonjak pada saat yang sama dengan waktu proses, pengumpulan sampah menyebabkan penggunaan CPU yang tinggi. Jika tidak, profilkan aplikasi untuk menemukan di mana penggunaan tinggi terjadi.