Możesz cofnąć się i zobaczyć, skąd i dlaczego pochodzą te istniejące modele. Kiedy proces jest tworzony, otrzymuje się po prostu płaski obszar pamięci, który jest po prostu indeksowany od 0 do N. Ponieważ ten obszar pamięci (tutaj mówimy o pamięci RAM) jest wspierany przez dedykowany sprzęt i niektóre fantazyjne półprzewodniki, zdarza się, że jest dość szybki, ale to nie jedyny w swoim rodzaju. Inne urządzenia, takie jak dyski twarde, są w gruncie rzeczy tym samym, płaską przestrzeń adresowaną przez indeks, ale o wiele rzędów wielkości wolniejszą.
Powodem istnienia „sterty” jest to, że dla każdej aplikacji próba samodzielnego zarządzania wykorzystaniem pamięci RAM byłaby niepraktyczna. Dawno temu, dokładnie tak się stało, programiści zaplanowali z wyprzedzeniem dokładnie to, do czego będzie używana każda lokalizacja pamięci RAM. Gdy oprogramowanie stało się bardziej złożone, ktoś powiedział: czy nie byłoby miło, gdybym mógł przejść do jakiejś czarnej skrzynki i powiedzieć „Potrzebuję 10 bajtów, więc daj mi” i nie martwić się o wszystkie skomplikowane szczegóły dotyczące tego, gdzie i jak te 10 bajtów pochodzą lub w jaki sposób są odzyskiwane. Taka jest kupa, tak naprawdę nie staje się bardziej podstawowa.
Za każdym razem, gdy tworzony jest wątek, istnieją pewne struktury danych (i stos), które są pozyskiwane przy użyciu tej samej „operacji gimme”, którą właśnie opisałem. Stos prawie powszechnie używany, ponieważ idealnie pasuje do ramek stosów wywołań funkcji i ich natury LIFO. Teoretycznie każde wywołanie funkcji i zmienne lokalne mogą być przydzielone na stercie, ale byłoby to po prostu zbyt drogie, w porównaniu z zaledwie kilkoma instrukcjami asemblacji potrzebnymi do aktualizacji rejestru wskaźnika stosu (ESP na x86).
Lokalna pamięć wątków (TLS) jest również budowana na szczycie stosu. Gdy wątek jest tworzony, jako część podróży na stertę w celu alokacji pamięci dla struktur zarządzania, oddzielna przestrzeń dla TLS jest również przydzielana ze sterty.
Tak więc ostatecznie wszystko, co naprawdę masz, to ogólny przydział pamięci (tj. Sterta), a wszystko inne to wyspecjalizowana forma. Innymi słowy, jeśli chcesz zrezygnować z jakiegoś aspektu „Chcę przeznaczyć tyle (lub tak mało), ile chcę, zachowaj to tak długo, jak chcę i za darmo, kiedy chcę”, możesz uniknąć handlu wyłącza ogólny rozdzielacz sterty dla innego modelu, który oferuje prędkość, ale kosztem innych ograniczeń.
Weź stos. Jest niesamowicie szybki w porównaniu ze stertą, ale dwa kompromisy to 1) nie kontrolujesz, kiedy pamięć zostanie zwolniona; zamiast tego po wyjściu z funkcji, cokolwiek przydzielono, zniknęło i 2), ponieważ stosy są zwykle ograniczone, powinieneś ostrożnie alokować duże ilości danych bezpośrednio na stosie.
Innym rodzajem „modelu pamięci” jest Virtual Memory Manager (VMM) oferowany przez prawie każdy główny system operacyjny za pośrednictwem wywołań systemowych. VMM jest bardzo podobny do sterty w tym sensie, że możesz poprosić o dowolną ilość pamięci i zachować ją tak długo, jak chcesz. Ograniczeniem jest jednak to, że pamięć można alokować tylko w wielokrotnościach wielkości strony (np. 4KB), więc bezpośrednie użycie VMM spowodowałoby duże obciążenie w typowej aplikacji, która często alokuje 8-24 bajtów na raz. W rzeczywistości prawie każda implementacja sterty jest zbudowana na VMM specjalnie w celu umożliwienia bardzo ogólnego, niewyspecjalizowanego przydzielania małych bloków. Sterta trafia do VMM, gdy potrzebuje więcej pamięci, a następnie wykleja wiele małych fragmentów tej pamięci do aplikacji.
Jeśli masz aplikację, która wymaga alokacji dużych bloków, możesz rozważyć przejście bezpośrednio do VMM, chociaż niektóre sterty zawierają instrukcję if wewnątrz malloc (), a jeśli rozmiar bloku jest większy niż pewien próg, po prostu przechodzą do VMM dla Was.
Inną formą alokatorów zamiast bezpośredniego używania sterty byłyby pule. Pula to specjalistyczny alokator, w którym wszystkie bloki mają ten sam rozmiar. Pule (podobnie jak stos i TLS) są zbudowane na stercie lub VMM. Baseny są przydatne w miejscach, w których przeznaczasz dużo (miliony) krótkotrwałych, małych obiektów o tej samej wielkości. Pomyśl o usłudze sieciowej przetwarzającej przychodzące żądania. Każde żądanie klienta może spowodować przydzielenie tej samej N-bajtowej struktury do obsługi tego żądania. Kompromis z używaniem pul polega na tym, że każda pula obsługuje tylko jeden rozmiar bloku (ale można utworzyć wiele pul). Zaletą pul jest to, że ponieważ wszystkie obiekty mają taki sam rozmiar, nie wymaga skomplikowanej logiki. Zamiast tego, ilekroć potrzebujesz nowego bloku, po prostu daje ci ten, który został niedawno uwolniony.
I na koniec, pamiętaj o tym dysku twardym, o którym wspominałem. Możesz mieć model pamięci, który zachowuje się jak system plików i powiela to samo pojęcie pozycji katalogu i i-węzłów, aby umożliwić hierarchiczną alokację bloków danych, w których każdy blok danych jest adresowany ścieżką. To właśnie robi tmpfs .
Poza rzeczami, o których wspomniałem, jestem pewien, że istnieją inne bardziej wyspecjalizowane modele, ale w końcu, ponieważ wszystko opiera się na płaskiej przestrzeni adresowej (to jest do czasu, aż jakieś geneza wymyśli jakieś dziwne - $$ niepłaska przestrzeń ), wszystko wraca do tego ogólnego alokatora „gimme”, którym jest VMM lub sterta.