Niektóre śmieciarki (przynajmniej Mono i .NET) mają obszar pamięci krótkotrwałej, który często skanują, oraz obszar pamięci pomocniczej, który skanują rzadziej. Mono nazywa to żłobkiem.
Aby dowiedzieć się, które obiekty można usunąć, skanują wszystkie obiekty, zaczynając od korzeni, stosu i rejestrów, i usuwają wszystkie obiekty, do których nie ma już odniesienia.
Moje pytanie brzmi: w jaki sposób zapobiegają skanowaniu wszystkich używanych pamięci na każdym zbiorze? Zasadniczo jedynym sposobem, aby dowiedzieć się, które obiekty nie są już używane, jest zeskanowanie wszystkich obiektów i wszystkich ich odniesień. Zapobiegnie to jednak zamianie pamięci przez system operacyjny, mimo że nie jest ona używana przez aplikację i wydaje się, że trzeba wykonać ogrom pracy, także w przypadku „Nursery Collection”. Nie wydaje się, żeby dużo wygrywali, korzystając z pokoju dziecinnego.
Czy coś mi brakuje, czy śmieciarz faktycznie skanuje każdy obiekt i każde odwołanie za każdym razem, gdy wykonuje kolekcję?
źródło
Odpowiedzi:
Podstawowe obserwacje, które pozwalają pokoleniowemu śmieciu na śmieci, aby uniknąć konieczności skanowania wszystkich obiektów starszej generacji, to:
W wielu frameworkach GC śmieciarz może oflagować obiekty lub ich części w taki sposób, że pierwsza próba zapisu do nich wywoła specjalny kod, aby zarejestrować fakt, że zostały one zmodyfikowane. Obiekt lub jego część, który został zmodyfikowany, niezależnie od jego generacji, należy przeskanować w następnej kolekcji, ponieważ może zawierać odniesienia do nowszych obiektów. Z drugiej strony bardzo często zdarza się, że wiele starszych obiektów nie jest modyfikowanych między kolekcjami. Fakt, że skany niższej generacji mogą ignorować takie obiekty, może pozwolić, aby takie skany zakończyły się znacznie szybciej niż w innym przypadku.
Zauważ, przy okazji, że nawet jeśli nie można wykryć, kiedy obiekty są modyfikowane i trzeba będzie skanować wszystko na każdym przejściu GC, generowanie odśmiecania może nadal poprawić wydajność etapu zamiatania kompaktora zbierającego. W niektórych środowiskach osadzonych (szczególnie w tych, w których istnieje niewielka lub żadna różnica prędkości między sekwencyjnym i losowym dostępem do pamięci), przenoszenie bloków pamięci jest stosunkowo drogie w porównaniu do oznaczania odnośników. W konsekwencji, nawet jeśli nie można przyspieszyć fazy „oznaczenia” za pomocą kolektora pokoleniowego, warto przyspieszyć fazę „zamiatania”.
źródło
GC, o których mówisz, to generatory śmieci. Zostały zaprojektowane tak, aby jak najlepiej wykorzystać obserwację zwaną „śmiertelnością niemowląt” lub „hipotezą pokoleniową”, co oznacza, że większość obiektów bardzo szybko staje się nieosiągalna. Rzeczywiście skanują, zaczynając od korzeni, ale ignorują wszystkie stare obiekty . Dlatego nie muszą skanować większości obiektów w pamięci, skanują tylko młode obiekty (kosztem niewykrycia nieosiągalnych starych obiektów, przynajmniej nie w tym momencie).
„Ale to źle”, słyszę, jak krzyczysz, „stare przedmioty mogą odnosić się do młodych przedmiotów”. Masz rację i istnieje kilka rozwiązań tego problemu, które polegają na szybkim i wydajnym zdobywaniu wiedzy, które stare obiekty należy sprawdzić, a które można bezpiecznie zignorować. Sprowadzają się one raczej do rejestrowania obiektów lub małych (większych niż obiekty, ale znacznie mniejszych niż cała kupa) zakresów pamięci, które zawierają wskaźniki dla młodszych pokoleń. Inni opisali je o wiele lepiej niż ja, więc dam ci tylko kilka słów kluczowych: znakowanie kart, zapamiętane zestawy, pisanie barier. Istnieją również inne techniki (w tym hybrydy), ale obejmują one powszechnie znane mi podejścia.
źródło
Aby dowiedzieć się, jakie obiekty przedszkolne są nadal aktywne, kolektor musi tylko zeskanować zestaw główny i wszystkie stare obiekty, które zostały zmutowane od ostatniej kolekcji , ponieważ stary obiekt, który nie został niedawno zmutowany, nie może wskazywać na młody obiekt . Istnieją różne algorytmy utrzymywania tej informacji na różnych poziomach dokładności (od dokładnego zestawu zmutowanych pól do zestawu stron, na których mogła wystąpić mutacja), ale ogólnie wszystkie one zawierają pewną barierę zapisu : kod, który działa przy każdym odwołaniu -typedowa mutacja polowa, która aktualizuje księgowość GC.
źródło
Najstarsza i najprostsza generacja śmieciarek faktycznie skanowała całą pamięć i musiała przerwać wszystkie inne procesy. Później algorytmy poprawiły to na różne sposoby - dzięki czemu kopiowanie / skanowanie jest przyrostowe lub działa równolegle. Większość współczesnych śmieciarek segreguje obiekty na pokolenia i ostrożnie zarządza wskaźnikami międzypokoleniowymi, dzięki czemu nowsze generacje można zbierać bez przeszkadzania starszym.
Kluczową kwestią jest to, że śmieciarze ściśle współpracują z kompilatorem i resztą środowiska wykonawczego, aby zachować iluzję, że obserwuje całą pamięć.
źródło
Zasadniczo ... GC używa „segmentów” do oddzielenia tego, co jest używane, a co nie. Gdy sprawi, że będzie sprawdzane, usuwa rzeczy, które nie są używane, i przenosi wszystko inne do drugiej generacji (która jest sprawdzana rzadziej niż pierwsza generacja), a następnie przenosi rzeczy, które są nadal używane w drugiej den do trzeciej generacji.
Tak więc, rzeczy w trzeciej generacji są zwykle obiektami, które z jakiegoś powodu są zablokowane, a GC nie sprawdza tam zbyt często.
źródło
Algorytmem zwykle stosowanym w tym GC jest Naiwna ocena i przegląd
powinieneś również zdawać sobie sprawę z faktu, że nie jest to zarządzane przez sam C #, ale przez tak zwane CLR .
źródło