Bagikan melalui


Pengumpulan dan performa sampah

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.

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.

Pemeriksaan performa
Tentukan berapa banyak memori virtual yang dapat dialokasikan.
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.

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 GcCondemnedGeneration ke generasi yang diinginkan. Perintah ini memerlukan simbol privat.

    Perintah ini memaksa penghentian jika RestartEE dijalankan 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:

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

  1. Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah cetak pengecualian (pe):

    !pe

    Jika 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):
    
  2. 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:

    ~\*kb

    Utas yang memiliki tumpukan dengan panggilan pengecualian ditunjukkan oleh argumen RaiseTheException. Ini adalah objek pengecualian terkelola.

    28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
    
  3. Anda dapat menggunakan perintah berikut untuk mencadangkan 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 dipesan

  • Di WinDbg dengan ekstensi debugger SOS dimuat, masukkan perintah berikut untuk mendapatkan wilayah gratis terbesar:

    !address -summary

    Wilayah gratis terbesar ditampilkan seperti yang ditunjukkan dalam 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 suatu segmen.

    -atau-

  • Gunakan perintah vmstat:

    !vmstat

    Wilayah 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

  1. Mulai Windows Task Manager.

  2. Pada tab Performance , lihat nilai yang diterapkan. (Di Windows 7, lihat Commit (KB) di System group.)

    Jika Total dekat dengan Limit, Anda hampir kehabisan memori fisik.

Untuk menentukan berapa banyak memori yang dialokasikan heap terkelola

  • Gunakan penghitung kinerja memori # Total committed bytes untuk 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 Heaps performa, 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 bytes performa memori.

    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 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 -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-objek besar di generasi kedua

  • 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 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 objects
    

    Objek 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 200

    Contoh 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 1c37b2ac

    Akar 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 484
    

    Perintah gcroot dapat 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, jadi gcroot harus 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

  1. Di debugger WinDbg atau Visual Studio dengan ekstensi debugger SOS dimuat, masukkan perintah berikut:

    !finalizequeue

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

  2. Untuk mendapatkan output utas, masukkan perintah berikut:

    !threads -special

    Perintah ini menyediakan 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 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 -stat

    Perintah 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 objects
    
  • Untuk menentukan ruang kosong di generasi 0, masukkan perintah berikut untuk mendapatkan informasi konsumsi memori berdasarkan generasi:

    !eeheap -gc

    Perintah 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-0x49521f8c

    Hasilnya adalah sebagai berikut. Generasi 0 sekitar 9 MB.

    Evaluate expression: 9321848 = 008e3d78
    
  • Perintah berikut mencadangkan 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 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:

    !gchandles

    Statistik 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 GC kinerja 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, dan Gen2 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. 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               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 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        GCRestartEEEnd
    

    Menangguhkan utas terkelola membutuhkan waktu 26us (GCSuspendEEEndGCSuspendEEBegin_V1).

    Pengumpulan sampah aktual membutuhkan waktu 4,8ms (GCEnd_V1GCStart_V1).

    Melanjutkan utas yang terkelola membutuhkan waktu 21us (GCRestartEEEndGCRestartEEBegin).

    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    4372
    

    Peristiwa GCStart_V1 di 42504816 menunjukkan bahwa ini adalah pengumpulan sampah latar belakang, karena bidang terakhir adalah 1. Ini adalah pengumpulan sampah No. 102019.

    Peristiwa ini GCStart terjadi 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 GCEnd untuk 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[])+0x48
    

    Jika 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[])+0x153
    

    Pembantu just-in-time (JIT_New*) akhirnya memanggil GCHeap::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 –stat

    Contoh 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 objects
    

    Ulangi perintah di akhir generasi 2:

    !dumpheap –stat

    Contoh 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 objects
    

    Objek 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 ini double[] adalah alasan mengapa pengumpulan sampah generasi 2 ini terjadi. Langkah Anda selanjutnya adalah menentukan mengapa double[] objek ada di sana dan mengapa mereka meninggal. Anda dapat meminta pengembang kode dari mana objek ini berasal, atau Anda dapat menggunakan gcroot perintah .

Untuk menentukan apakah penggunaan CPU tinggi disebabkan oleh pengumpulan sampah

  • Menghubungkan nilai penghitung % Time in GC kinerja memori dengan waktu proses.

    % Time in GC Jika 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.

Lihat juga