Beberapa (paling tidak Mono dan .NET) pemulung memiliki area memori jangka pendek yang sering mereka pindai, dan area memori sekunder yang jarang mereka pindai. Mono menyebut ini kamar anak-anak.
Untuk mengetahui objek yang dapat dibuang, mereka memindai semua objek mulai dari root, tumpukan dan register dan membuang semua objek yang tidak dirujuk lagi.
Pertanyaan saya adalah bagaimana mereka mencegah semua memori terpakai dipindai pada setiap pengambilan? Pada prinsipnya, satu-satunya cara untuk mengetahui objek apa yang tidak digunakan lagi adalah memindai semua objek dan semua referensi mereka. Namun, ini akan mencegah OS dari menukar memori meskipun itu tidak digunakan oleh aplikasi dan terasa seperti sejumlah besar pekerjaan yang perlu dilakukan, juga untuk "Koleksi Pembibitan". Tidak terasa mereka menang banyak dengan menggunakan kamar bayi.
Apakah saya kehilangan sesuatu atau apakah pemulung benar-benar memindai setiap objek dan setiap referensi setiap kali melakukan pengumpulan?
sumber
Jawaban:
Pengamatan mendasar yang memungkinkan pengumpulan sampah generasi untuk menghindari keharusan memindai semua objek generasi yang lebih tua adalah:
Dalam banyak kerangka kerja GC, mungkin bagi pemulung untuk menandai objek atau bagiannya sedemikian rupa sehingga upaya pertama untuk menulis kepada mereka akan memicu kode khusus untuk mencatat fakta bahwa mereka telah dimodifikasi. Objek atau bagiannya yang telah dimodifikasi, terlepas dari generasinya, harus dipindai dalam koleksi berikutnya, karena dapat berisi referensi ke objek yang lebih baru. Di sisi lain, sangat umum untuk ada banyak objek yang lebih tua yang tidak bisa dimodifikasi di antara koleksi. Fakta bahwa pemindaian generasi yang lebih rendah dapat mengabaikan objek tersebut dapat memungkinkan pemindaian tersebut untuk menyelesaikan lebih cepat daripada yang seharusnya.
Perhatikan, btw, bahwa bahkan jika seseorang tidak dapat mendeteksi kapan objek dimodifikasi dan harus memindai semuanya pada setiap pass GC, pengumpulan sampah generasi masih dapat meningkatkan kinerja tahap "sweep" dari kolektor pemadatan. Dalam beberapa lingkungan tertanam (terutama di mana ada sedikit atau tidak ada perbedaan dalam kecepatan antara akses memori sekuensial dan acak), memindahkan blok memori sekitar relatif mahal dibandingkan dengan menandai tag. Akibatnya, bahkan jika fase "tanda" tidak dapat dipercepat menggunakan kolektor generasi, mempercepat fase "sapuan" mungkin bermanfaat.
sumber
GC yang Anda maksud adalah pengumpul sampah generasi . Mereka direkayasa untuk mendapatkan hasil maksimal dari pengamatan yang dikenal sebagai "kematian bayi" atau "hipotesis generasi", yang berarti bahwa sebagian besar objek menjadi tidak terjangkau dengan sangat cepat. Mereka memang memindai mulai dari akar, tetapi mengabaikan semua benda lama . Oleh karena itu, mereka tidak perlu memindai sebagian besar objek dalam memori, mereka hanya memindai objek muda (dengan mengorbankan tidak mendeteksi objek lama yang tidak dapat dijangkau, setidaknya tidak pada saat itu).
"Tapi itu salah", saya mendengar Anda berteriak, "benda-benda tua dapat dan memang merujuk pada benda-benda muda". Anda benar, dan ada beberapa solusi untuk itu, yang semuanya berputar di sekitar mendapatkan pengetahuan, dengan cepat dan efisien, benda-benda tua mana yang harus diperiksa dan mana yang aman untuk diabaikan. Mereka cukup banyak mendidih ke objek rekaman, atau rentang memori kecil (lebih besar dari objek, tetapi jauh lebih kecil dari seluruh tumpukan) yang berisi pointer ke generasi yang lebih muda. Orang lain telah menggambarkan hal-hal yang jauh lebih baik daripada saya, jadi saya hanya akan memberi Anda beberapa kata kunci: Penandaan kartu, set ingat, menulis hambatan. Ada teknik lain juga (termasuk hibrida), tetapi ini mencakup pendekatan umum yang saya ketahui.
sumber
Untuk mengetahui objek pembibitan apa yang masih hidup, kolektor hanya perlu memindai kumpulan root dan benda lama yang telah dimutasi sejak koleksi terakhir , karena objek lama yang belum mutasi baru-baru ini tidak dapat mengarah ke objek muda . Ada berbagai algoritme untuk mempertahankan informasi ini pada berbagai tingkat presisi (dari set bidang mutasi yang tepat hingga set halaman di mana mutasi mungkin terjadi), tetapi semuanya umumnya melibatkan semacam semacam penghalang tulis : kode yang berjalan pada setiap referensi mutasi bidang -typed yang memperbarui pembukuan GC.
sumber
Generasi pengumpul sampah tertua dan paling sederhana benar-benar memindai semua memori, dan harus menghentikan semua pemrosesan lainnya saat mereka melakukannya. Algoritme kemudian ditingkatkan dalam hal ini dengan berbagai cara - membuat penambahan / pemindaian, atau berjalan secara paralel. Sebagian besar pengumpul sampah modern memisahkan objek menjadi beberapa generasi, dan mengelola dengan hati-hati petunjuk lintas generasi sehingga generasi yang lebih baru dapat dikumpulkan tanpa mengganggu yang lebih tua.
Poin kuncinya adalah bahwa pengumpul sampah bekerja dalam kolaborasi erat dengan kompiler dan dengan sisa runtime untuk mempertahankan ilusi bahwa ia mengawasi semua memori.
sumber
Pada dasarnya ... GC menggunakan "ember" untuk memisahkan apa yang sedang digunakan dan apa yang tidak. Setelah membuatnya memeriksa, menghapus hal-hal yang tidak digunakan dan memindahkan semuanya ke generasi ke-2 (yang jarang diperiksa dari generasi ke-1) dan kemudian memindahkan hal-hal yang masih digunakan di ruang ke-2 ke gen ke-3.
Jadi, hal-hal dalam generasi ke-3 biasanya benda yang macet karena alasan tertentu, dan GC tidak sering memeriksa di sana.
sumber
Algoritma yang biasanya digunakan oleh GC ini adalah Naïve mark-and-sweep
Anda juga harus menyadari fakta bahwa ini bukan dikelola oleh C # itu sendiri, tetapi oleh yang disebut CLR .
sumber