Meningkatkan performa pengumpulan sampah di aplikasi WinUI

Aplikasi WinUI yang dibangun dengan Windows App SDK dan ditulis dalam C# mendapatkan manajemen memori otomatis dari pengumpul sampah .NET. Artikel ini merangkum perilaku dan praktik terbaik performa untuk pengumpul sampah .NET di aplikasi WinUI. Untuk informasi selengkapnya tentang cara kerja pengumpul sampah .NET dan alat untuk men-debug dan menganalisis performa pengumpul sampah, lihat Pengumpulan sampah.

Nota

Membutuhkan intervensi dalam perilaku default pengelola sampah menunjukkan secara kuat adanya masalah memori umum pada aplikasi Anda. Gunakan alat Penggunaan Memori di Visual Studio dan panduan dalam Pengumpulan Sampah dan Kinerja untuk mengidentifikasi objek yang bertahan dari koleksi.

Pengumpul sampah menentukan kapan harus berjalan dengan menyeimbangkan konsumsi memori tumpukan terkelola dengan jumlah pekerjaan yang perlu dilakukan pengumpulan sampah. Salah satu cara pengumpul sampah melakukan ini adalah dengan membagi tumpukan menjadi generasi dan mengumpulkan hanya sebagian besar tumpukan sebagian besar waktu. Ada tiga generasi dalam tumpukan terkelola:

  • Generasi 0. Generasi ini berisi objek yang baru dialokasikan kecuali berukuran 85 KB atau lebih besar, dalam hal ini objek tersebut adalah bagian dari timbunan objek besar. Timbunan objek besar dikumpulkan dengan koleksi generasi 2. Koleksi Generasi 0 adalah jenis koleksi yang paling sering terjadi dan membersihkan objek berumur pendek seperti variabel lokal.
  • Generasi 1. Generasi ini berisi objek yang telah bertahan dari koleksi generasi 0. Ini berfungsi sebagai buffer antara generasi 0 dan generasi 2. Koleksi Generasi 1 terjadi lebih jarang daripada Koleksi Generasi 0 dan membersihkan objek sementara yang aktif selama Koleksi Generasi 0 sebelumnya. Koleksi generasi 1 juga mengumpulkan generasi 0.
  • Generasi 2. Generasi ini berisi objek berumur panjang yang telah bertahan dari koleksi generasi 0 dan generasi 1. Koleksi Generasi 2 adalah yang paling tidak sering dan mengumpulkan seluruh tumpukan terkelola, termasuk tumpukan objek besar, yang berisi objek yang 85 KB atau lebih besar.

Anda dapat mengukur performa pengumpul sampah dalam dua aspek: waktu yang diperlukan untuk melakukan pengumpulan sampah, dan konsumsi memori tumpukan yang dikelola. Jika Anda memiliki aplikasi kecil dengan ukuran tumpukan kurang dari 100 MB, maka fokuslah pada pengurangan konsumsi memori. Jika Anda memiliki aplikasi dengan tumpukan terkelola yang lebih besar dari 100 MB, maka fokus pada pengurangan waktu pengumpulan sampah saja. Berikut adalah cara Anda dapat membantu pengumpul sampah .NET mencapai performa yang lebih baik.

Mengurangi konsumsi memori

Referensi Pelepasan

Referensi ke objek di aplikasi Anda mencegah objek tersebut, dan semua objek yang direferensikannya, dikumpulkan. Pengkompilasi .NET melakukan pekerjaan yang baik untuk mendeteksi ketika variabel tidak lagi digunakan sehingga objek yang dipegang oleh variabel tersebut akan memenuhi syarat untuk pengumpulan. Tetapi dalam beberapa kasus mungkin tidak jelas bahwa beberapa objek memiliki referensi ke objek lain karena bagian dari grafik objek mungkin dimiliki oleh pustaka yang digunakan aplikasi Anda. Untuk mempelajari tentang alat dan teknik untuk mengetahui objek mana yang bertahan dari pengumpulan sampah, lihat Pengumpulan dan performa sampah.

Mendorong pengumpulan sampah jika berguna

Mengaktifkan pengumpulan sampah hanya setelah Anda mengukur performa dari aplikasi Anda dan telah menentukan bahwa mengaktifkan pengumpulan tersebut akan meningkatkan kinerja aplikasi.

Anda dapat menginduksi pengumpulan sampah generasi dengan memanggil GC. Collect(n), di mana n adalah generasi yang ingin Anda kumpulkan (0, 1, atau 2).

Nota

Kami menyarankan agar Anda tidak memaksa pengumpulan sampah di aplikasi Anda karena pengumpul sampah menggunakan banyak heuristik untuk menentukan waktu terbaik untuk melakukan koleksi, dan memaksa pengumpulan dalam banyak kasus penggunaan CPU yang tidak perlu. Tetapi jika Anda tahu bahwa Anda memiliki sejumlah besar objek di aplikasi Anda yang tidak lagi digunakan dan Anda ingin mengembalikan memori ini ke sistem, maka mungkin tepat untuk memaksa pengumpulan sampah. Misalnya, Anda dapat menginduksi koleksi di akhir urutan pemuatan dalam game untuk membebaskan memori sebelum gameplay dimulai.   Untuk menghindari secara tidak sengaja menginduksi terlalu banyak koleksi sampah, Anda dapat mengatur GCCollectionMode ke Optimized. Ini menginstruksikan pengumpul sampah untuk memulai koleksi hanya jika menentukan bahwa koleksi akan cukup produktif untuk dibenarkan.

Mengurangi waktu pengumpulan sampah

Bagian ini berlaku jika Anda telah menganalisis aplikasi dan mengamati waktu pengumpulan sampah yang besar. Waktu jeda terkait pengumpulan sampah mencakup waktu yang diperlukan untuk menjalankan satu pass pengumpulan sampah dan total waktu yang dihabiskan aplikasi Anda untuk melakukan pengumpulan sampah. Jumlah waktu yang diperlukan untuk melakukan koleksi tergantung pada berapa banyak data langsung yang harus dianalisis oleh pengumpul. Generasi 0 dan generasi 1 dibatasi ukurannya, tetapi generasi 2 terus tumbuh karena lebih banyak objek berumur panjang aktif di aplikasi Anda. Ini berarti bahwa waktu pengumpulan untuk generasi 0 dan generasi 1 terikat, sementara koleksi generasi 2 dapat memakan waktu lebih lama. Seberapa sering pengumpulan sampah berjalan sebagian besar tergantung pada berapa banyak memori yang Anda alokasikan, karena pengumpulan sampah membebaskan memori untuk memenuhi permintaan alokasi.

Pengumpul sampah kadang-kadang menjeda aplikasi Anda untuk melakukan pekerjaan, tetapi tidak selalu menjeda aplikasi Anda sepanjang waktu melakukan koleksi. Waktu jeda biasanya tidak dapat dirasakan pengguna di aplikasi Anda, terutama untuk koleksi generasi 0 dan generasi 1. Fitur pengumpulan sampah Latar Belakang dari pengumpul sampah .NET memungkinkan koleksi generasi 2 dilakukan secara bersamaan saat aplikasi Anda berjalan dan hanya akan menjeda aplikasi Anda untuk waktu yang singkat. Tetapi tidak selalu mungkin untuk melakukan koleksi generasi 2 sebagai koleksi latar belakang. Dalam hal ini, jeda dapat dirasakan pengguna jika Anda memiliki tumpukan yang cukup besar (lebih dari 100 MB).

Pengumpulan sampah yang sering dapat berkontribusi pada peningkatan CPU, peningkatan konsumsi daya, waktu pemuatan yang lebih lama, atau penurunan kecepatan bingkai dalam aplikasi Anda. Di bawah ini adalah beberapa teknik yang dapat Anda gunakan untuk mengurangi waktu pengumpulan sampah dan jeda terkait pengumpulan di aplikasi WinUI terkelola Anda.

Mengurangi alokasi memori

Jika Anda tidak mengalokasikan objek apa pun, maka pengumpul sampah tidak berjalan kecuali ada kondisi memori rendah dalam sistem. Mengurangi jumlah memori yang Anda alokasikan secara langsung diterjemahkan ke pengumpulan sampah yang lebih jarang.

Jika jeda dalam beberapa bagian aplikasi Anda sama sekali tidak diinginkan, maka Anda dapat mengalokasikan terlebih dahulu objek yang diperlukan pada waktu yang kurang kritis bagi performa. Misalnya, permainan mungkin mengalokasikan semua objek yang diperlukan untuk gameplay selama layar pemuatan sebuah level dan tidak membuat alokasi selama permainan berlangsung. Ini menghindari jeda saat pengguna memainkan game dan dapat menghasilkan kecepatan bingkai yang lebih tinggi dan lebih konsisten.

Mengurangi koleksi generasi 2 dengan menghindari objek dengan masa pakai menengah

Koleksi sampah generasi performa terbaik ketika Anda memiliki objek yang benar-benar berumur pendek dan/atau benar-benar berumur panjang di aplikasi Anda. Objek berumur pendek dikumpulkan dalam koleksi generasi 0 dan generasi 1 yang lebih murah, dan objek yang berumur panjang dipromosikan ke generasi 2, yang jarang dikumpulkan. Objek berumur panjang adalah objek yang digunakan selama seluruh durasi aplikasi Anda, atau selama periode aplikasi yang signifikan, seperti selama halaman atau tingkat permainan tertentu.

Jika Anda sering membuat objek yang memiliki masa pakai sementara tetapi hidup cukup lama untuk dipromosikan ke generasi 2, maka lebih banyak koleksi generasi 2 yang mahal terjadi. Anda mungkin dapat mengurangi koleksi generasi 2 dengan mendaur ulang objek yang ada atau merilis objek dengan lebih cepat.

Contoh umum objek dengan masa pakai jangka menengah adalah objek yang digunakan untuk menampilkan item dalam daftar yang digulir pengguna. Jika objek dibuat saat item dalam daftar digulir ke tampilan, dan tidak lagi direferensikan saat item dalam daftar digulir keluar dari tampilan, aplikasi Anda biasanya memiliki sejumlah besar koleksi memori generasi 2. Dalam situasi seperti ini, Anda dapat melakukan pra-alokasi dan menggunakan kembali sekumpulan objek untuk data yang tengah aktif ditampilkan kepada pengguna, dan menggunakan objek berumur pendek untuk memuat info saat item dalam daftar muncul.

Kurangi koleksi generasi 2 dengan menghindari objek berukuran besar dengan masa pakai pendek

Objek apa pun yang berukuran 85 KB atau lebih besar dialokasikan pada timbunan objek besar (LOH) dan dikumpulkan sebagai bagian dari generasi 2. Jika Anda memiliki variabel sementara, seperti buffer, yang lebih besar dari 85 KB, maka koleksi generasi 2 membersihkannya. Membatasi variabel sementara hingga kurang dari 85 KB mengurangi jumlah koleksi generasi 2 di aplikasi Anda. Salah satu teknik umumnya adalah membuat kumpulan buffer dan menggunakan kembali objek dari kumpulan untuk menghindari alokasi sementara yang besar.

Hindari objek yang kaya referensi

Pengumpul sampah menentukan objek-objek yang masih hidup dengan mengikuti referensi antara objek, dimulai dari akar di aplikasi Anda. Untuk informasi selengkapnya, lihat Apa yang terjadi selama pengumpulan sampah. Jika objek berisi banyak referensi, maka ada lebih banyak pekerjaan yang harus dilakukan pengumpul sampah. Teknik umum, terutama dengan objek besar, adalah mengonversi objek yang kaya referensi menjadi objek tanpa referensi. Misalnya, alih-alih menyimpan referensi, simpan indeks. Tentu saja teknik ini hanya berfungsi ketika secara logis memungkinkan untuk melakukannya.

Mengganti referensi objek dengan indeks dapat menjadi perubahan yang mengganggu dan rumit pada aplikasi Anda dan paling efektif untuk objek besar dengan sejumlah besar referensi. Lakukan ini hanya jika Anda melihat waktu pengumpulan sampah besar di aplikasi Anda yang terkait dengan objek yang berat referensi.