Untungnya, seperti yang Anda tunjukkan, build COMPACT Mono menggunakan GC generasi (sangat kontras dengan yang Microsoft, seperti WinMo / WinPhone / XBox, yang hanya mempertahankan daftar datar).
Jika gim Anda sederhana, GC harus menanganinya dengan baik, tetapi berikut adalah beberapa petunjuk yang mungkin ingin Anda perhatikan.
Optimalisasi Dini
Pertama, pastikan ini benar-benar masalah bagi Anda sebelum mencoba memperbaikinya.
Jenis referensi mahal Pooling
Anda harus mengumpulkan jenis referensi yang sering Anda buat, atau yang memiliki struktur dalam. Contoh masing-masing adalah:
- Dibuat sering:
Bullet
Objek dalam permainan peluru-neraka .
- Struktur dalam: Pohon keputusan untuk implementasi AI.
Anda harus menggunakan Stack
sebagai kumpulan Anda (tidak seperti kebanyakan implementasi yang menggunakan a Queue
). Alasan untuk ini adalah karena dengan aStack
jika Anda mengembalikan objek ke kolam dan sesuatu yang lain segera meraihnya; itu akan memiliki peluang yang jauh lebih tinggi untuk berada di halaman aktif - atau bahkan di cache CPU jika Anda beruntung. Hanya saja itu sedikit lebih cepat. Selain itu selalu ukuran batas kolam Anda (abaikan saja 'checkin' jika batas Anda telah terlampaui).
Hindari Membuat Daftar Baru untuk Membersihkan Mereka
Jangan membuat yang baru List
ketika Anda benar-benar bermaksud untuk Clear()
itu. Anda dapat menggunakan kembali array backend dan menyimpan banyak alokasi dan salinan array. Demikian pula dengan percobaan ini dan buat daftar dengan kapasitas awal yang bermakna (ingat, ini bukan batas - hanya kapasitas awal) - tidak perlu akurat, hanya perkiraan. Ini pada dasarnya berlaku untuk semua jenis koleksi - kecuali untuk aLinkedList
.
Gunakan Struct Arrays (atau Daftar) Di Mana Mungkin
Anda mendapatkan sedikit manfaat dari penggunaan struct (atau tipe nilai secara umum) jika Anda membagikannya di antara objek. Sebagai contoh, dalam kebanyakan sistem partikel 'baik', partikel individu disimpan dalam array besar: array dan dan indeks dilewatkan di sekitar bukan partikel itu sendiri. Alasan ini bekerja sangat baik adalah karena ketika GC perlu mengumpulkan array, ia dapat melewatkan konten sepenuhnya (itu array primitif - tidak ada hubungannya di sini). Jadi alih-alih melihat 10.000 objek, GC hanya perlu melihat 1 array: untung besar! Sekali lagi, ini hanya akan bekerja dengan tipe nilai .
Setelah RoyT. memberikan beberapa umpan balik yang layak dan konstruktif yang saya rasa perlu saya kembangkan lebih lanjut. Anda hanya harus menggunakan teknik ini ketika Anda berurusan dengan entitas dalam jumlah besar (ribuan hingga puluhan ribu). Selain itu, harus berupa struct yang tidak boleh memiliki bidang jenis referensi dan harus hidup dalam array yang diketik secara eksplisit. Bertentangan dengan umpan baliknya, kami menempatkannya dalam array yang sangat mungkin merupakan bidang dalam kelas - yang berarti bahwa itu akan mendapatkan heap (kami tidak berusaha untuk menghindari alokasi tumpukan - hanya menghindari pekerjaan GC). Kami benar-benar peduli adalah kenyataan bahwa itu adalah sepotong memori yang berdekatan dengan banyak nilai yang dapat dilihat dengan mudah oleh GC dalam suatu O(1)
operasi alih-alih O(n)
operasi.
Anda juga harus mengalokasikan array ini sedekat mungkin dengan startup aplikasi Anda untuk mengurangi kemungkinan terjadinya fragmentasi , atau kerja berlebihan ketika GC mencoba untuk memindahkan potongan-potongan ini, (dan pertimbangkan untuk menggunakan daftar tautan hybrid alih-alih List
jenis bawaan. ).
GC.Collect ()
Ini jelas merupakan cara TERBAIK untuk menembak diri sendiri (lihat: "Pertimbangan Kinerja") dengan GC generasi. Anda hanya boleh menyebutnya ketika Anda telah membuat jumlah EXTREME sampah - dan satu contoh di mana itu bisa menjadi masalah adalah hanya setelah Anda memuat konten untuk level - dan bahkan kemudian Anda mungkin hanya perlu mengumpulkan generasi pertama ( GC.Collect(0);
) mudah-mudahan mencegah mempromosikan objek ke generasi ketiga.
IDisposable dan Field Nulling
Berguna untuk membatalkan bidang ketika Anda tidak lagi membutuhkan objek (lebih-lebih pada objek terbatas). Alasannya adalah dalam rincian tentang cara kerja GC: hanya menghapus objek yang tidak di-rooting (yaitu direferensikan) bahkan jika objek itu akan dicabut karena objek lain yang dihapus dalam koleksi saat ini ( catatan: ini tergantung pada GC rasa digunakan - beberapa benar-benar membersihkan rantai). Selain itu, jika suatu objek bertahan koleksi, itu segera dipromosikan ke generasi berikutnya - ini berarti bahwa setiap benda yang tertinggal di ladang akan dipromosikan selama koleksi. Setiap generasi berturut-turut secara eksponensial lebih mahal untuk dikumpulkan (dan jarang terjadi).
Ambil contoh berikut:
MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// G1 Collection
MyNestObject (G2) -> MyFurtherNestedObject (G2)
// G2 Collection
MyFurtherNestedObject (G3)
Jika MyFurtherNestedObject
terdapat objek multi-megabyte, Anda dapat dijamin bahwa GC tidak akan melihatnya cukup lama - karena Anda secara tidak sengaja mempromosikannya ke G3. Bandingkan dengan contoh ini:
MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// Dispose
MyObject (G1)
MyNestedObject (G1)
MyFurtherNestedObject (G1)
// G1 Collection
Pola Disposer membantu Anda mengatur cara yang dapat diprediksi untuk meminta objek untuk menghapus bidang pribadi mereka. Sebagai contoh:
public class MyClass : IDisposable
{
private MyNestedType _nested;
// A finalizer is only needed IF YOU CONTROL UNMANAGED RESOURCES
// ~MyClass() { }
public void Dispose()
{
_nested = null;
}
}