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.

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.

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

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:

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

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

  1. Mulai Pengelola Tugas Windows.

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

    Total Jika dekat dengan Limit, 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, jadi gcroot 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

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

  2. 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, 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 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 ( GCSuspendEEEndGCSuspendEEBegin_V1 ).

    Pengumpulan sampah sebenarnya membutuhkan waktu 4,8 md ( GCEnd_V1GCStart_V1 ).

    Melanjutkan utas terkelola membutuhkan waktu 21us ( GCRestartEEEndGCRestartEEBegin ).

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

Lihat juga