Czytałem o pokoleniach i stosie dużych obiektów. Ale nadal nie rozumiem, jakie jest znaczenie (lub korzyść) posiadania dużej sterty obiektów?
Co mogłoby pójść nie tak (pod względem wydajności lub pamięci), gdyby środowisko CLR po prostu polegało na generacji 2 (biorąc pod uwagę, że próg dla Gen0 i Gen1 jest mały do obsługi dużych obiektów) do przechowywania dużych obiektów?
.net
garbage-collection
clr
large-object-heap
Manish Basantani
źródło
źródło
Odpowiedzi:
Wyrzucanie elementów bezużytecznych nie tylko usuwa obiekty, do których nie istnieją odniesienia, ale także kompaktuje stertę. To bardzo ważna optymalizacja. Nie tylko zwiększa to wykorzystanie pamięci (brak nieużywanych dziur), ale także znacznie zwiększa wydajność pamięci podręcznej procesora. Pamięć podręczna to naprawdę poważna sprawa w przypadku nowoczesnych procesorów, są one o rząd wielkości szybsze niż szyna pamięci.
Kompaktowanie odbywa się po prostu przez kopiowanie bajtów. To jednak wymaga czasu. Im większy obiekt, tym większe prawdopodobieństwo, że koszt jego skopiowania przeważa nad możliwą poprawą wykorzystania pamięci podręcznej procesora.
Przeprowadzili więc szereg testów porównawczych, aby określić próg rentowności. I osiągnął 85 000 bajtów jako punkt odcięcia, w którym kopiowanie nie poprawia już wydajności. Ze specjalnym wyjątkiem dla tablic double, są one uważane za „duże”, gdy tablica ma więcej niż 1000 elementów. To kolejna optymalizacja dla 32-bitowego kodu, alokator dużych stert obiektów ma specjalną właściwość polegającą na tym, że przydziela pamięć pod adresy, które są wyrównane do 8, w przeciwieństwie do zwykłego alokatora pokoleń, który przydziela tylko wyrównane do 4. Takie wyrównanie jest wielką sprawą dla podwójnych , czytanie lub pisanie źle wyrównanego podwójnego jest bardzo kosztowne. Co dziwne, rzadkie informacje firmy Microsoft nigdy nie wspominają o tablicach długich, nie jestem pewien, o co chodzi.
Fwiw, jest wiele obaw programistów co do tego, że sterta dużych obiektów nie jest kompaktowana. To niezmiennie jest wyzwalane, gdy piszą programy, które zajmują ponad połowę całej dostępnej przestrzeni adresowej. Następnie za pomocą narzędzia takiego jak profiler pamięci, aby dowiedzieć się, dlaczego program zbombardował, mimo że wciąż było dostępnych dużo nieużywanej pamięci wirtualnej. Takie narzędzie pokazuje dziury w LOH, nieużywane fragmenty pamięci, w których wcześniej mieszkał duży obiekt, ale zbierano śmieci. Taka jest nieunikniona cena LOH, dziura może być ponownie wykorzystana tylko przez alokację dla obiektu o równym lub mniejszym rozmiarze. Prawdziwy problem polega na założeniu, że program powinien mieć możliwość wykorzystania całej pamięci wirtualnej w dowolnym momencie.
Problem, który w przeciwnym razie znika całkowicie po uruchomieniu kodu w 64-bitowym systemie operacyjnym. Proces 64-bitowy ma 8 terabajtów dostępnej przestrzeni adresowej pamięci wirtualnej, o 3 rzędy wielkości więcej niż proces 32-bitowy. Po prostu nie możesz zabraknąć dziur.
Krótko mówiąc, LOH sprawia, że kod działa wydajniej. Kosztem mniej wydajnego wykorzystania dostępnej przestrzeni adresowej pamięci wirtualnej.
UPDATE, .NET 4.5.1 obsługuje teraz kompaktowanie właściwości LOH, GCSettings.LargeObjectHeapCompactionMode . Uważaj na konsekwencje, proszę.
źródło
Jeśli rozmiar obiektu jest większy niż pewna przypięta wartość (85000 bajtów w .NET 1), środowisko CLR umieszcza go w stosie dużych obiektów. To optymalizuje:
nigdy niejest rzadko kompaktowany)źródło
Zasadnicza różnica między stertą małych obiektów (SOH) i stertą dużych obiektów (LOH) polega na tym, że pamięć w SOH jest kompaktowana po zebraniu, a LOH nie, jak ilustruje ten artykuł . Kompaktowanie dużych obiektów kosztuje dużo. Podobnie jak w przykładach w artykule, powiedzmy, że przeniesienie bajtu w pamięci wymaga 2 cykli, a następnie kompaktowanie obiektu o wielkości 8 MB w komputerze 2 GHz wymaga 8 ms, co jest dużym kosztem. Biorąc pod uwagę, że duże obiekty (w większości przypadków tablice) są w praktyce dość powszechne, przypuszczam, że to jest powód, dla którego Microsoft przypina duże obiekty do pamięci i proponuje LOH.
BTW, zgodnie z tym postem , LOH zwykle nie generuje problemów z fragmentami pamięci.
źródło
Zasadą jest to, że jest mało prawdopodobne (i całkiem możliwe, że zły projekt), aby proces utworzył wiele dużych obiektów o krótkim czasie życia, więc środowisko CLR przydziela duże obiekty do oddzielnej sterty, na której uruchamia GC według innego harmonogramu niż zwykły stos. http://msdn.microsoft.com/en-us/magazine/cc534993.aspx
źródło
Nie jestem ekspertem od CLR, ale wyobrażam sobie, że posiadanie dedykowanej sterty dla dużych obiektów może zapobiec niepotrzebnym zamiataniu GC istniejących hałd pokoleniowych. Przydzielanie dużego obiektu wymaga znacznej ilości ciągłej wolnej pamięci. Aby to zapewnić z rozproszonych "dziur" w pryzmach pokoleń, należałoby często zagęszczać (które są wykonywane tylko w cyklach GC).
źródło