2012-05-07 20 views
51

Po przydzieleniu pamięci na stercie jedynym ograniczeniem jest wolna pamięć RAM (lub pamięć wirtualna). To sprawia, że ​​Gb pamięci.dlaczego rozmiar stosu jest tak ograniczony?

Dlaczego więc rozmiar stosu jest tak ograniczony (około 1 Mb)? Jaki techniczny powód uniemożliwia tworzenie naprawdę dużych obiektów na stosie?

Aktualizacja: Moim zamiarem nie może być jasne, nie chcą przeznaczyć ogromne obiektów na stosie i nie trzeba większy stosu. To pytanie to czysta ciekawość.

+0

Dlaczego praktyczne byłoby tworzenie dużych obiektów na stercie? (Łańcuchy wywoływania zazwyczaj idą na stos). – Makoto

+2

Myślę, że prawdziwa odpowiedź jest prostsza niż większość odpowiedzi przedstawia: "ponieważ tak zawsze robiliśmy i do tej pory wszystko było w porządku, więc po co zmieniać?" –

+0

@JerryCoffin Czy przeczytaliście którąkolwiek z zamieszczonych do tej pory odpowiedzi? Jest więcej wglądu w to pytanie. – user1202136

Odpowiedz

31

Moja intuicja jest następująca. Stos nie jest tak łatwy w zarządzaniu jak stertę. Stos musi być przechowywany w stałych lokalizacjach w pamięci. Oznacza to, że nie możesz losowo przydzielić stosu w razie potrzeby, ale musisz przynajmniej zarezerwować adresy wirtualne w tym celu. Im większy rozmiar zarezerwowanej wirtualnej przestrzeni adresowej, tym mniej wątków można utworzyć.

Na przykład aplikacja 32-bitowa ma zazwyczaj wirtualną przestrzeń adresową 2 GB. Oznacza to, że jeśli rozmiar stosu wynosi 2 MB (domyślnie w pthreads), można utworzyć maksymalnie 1024 wątków. Może to być niewielkie w przypadku aplikacji takich jak serwery WWW. Zwiększenie rozmiaru stosu do, powiedzmy, 100 MB (to znaczy, że zarezerwujesz 100 MB, ale niekoniecznie przydzielone 100 MB do stosu natychmiast), ograniczyłoby liczbę wątków do około 20, co może ograniczać nawet dla prostych aplikacji GUI.

Interesujące pytanie brzmi: dlaczego wciąż mamy ten limit na platformach 64-bitowych. Nie znam odpowiedzi, ale zakładam, że ludzie są już przyzwyczajeni do niektórych "dobrych praktyk": należy uważać, aby przydzielić ogromne przedmioty na stercie i, jeśli to konieczne, ręcznie zwiększyć rozmiar stosu. Dlatego nikt nie pomyślał o dodaniu "ogromnego" wsparcia stosu na 64-bitowych platformach.

+0

Wiele 64-bitowych maszyn ma tylko 48-bitowe adresy (z dużym zyskiem ponad 32-bitowe, ale nadal ograniczone). Nawet z dodatkową przestrzenią musisz się martwić o to, jak rezerwujesz w odniesieniu do tabel stron - to znaczy, że zawsze jest więcej miejsca na więcej miejsca. Jest to prawdopodobnie tak samo tanie, jeśli nie taniej, alokowanie nowego segmentu (mmap) zamiast rezerwowania ogromnych przestrzeni stosów dla każdego wątku. –

+2

@ edA-qamort-ora-y: Ta odpowiedź nie mówi o _własności_, chodzi o "rezerwację pamięci wirtualnej", która jest prawie darmowa, a na pewno o wiele większa niż mmap. –

3

Po pierwsze, stos jest ciągły, więc jeśli przydzieli się 12 MB, należy usunąć 12 MB, gdy chcemy zejść poniżej tego, co zostało utworzone. Również poruszające się obiekty stają się znacznie trudniejsze. Oto przykład z prawdziwego świata, który może ułatwić zrozumienie:

Załóżmy, że układasz pudełka wokół pokoju. Co jest łatwiejsze do zarządzania:

  • układanie skrzynek dowolnej masie na szczycie siebie, ale kiedy trzeba coś na dole trzeba cofnąć całą stertę. Jeśli chcesz wydobyć przedmiot ze stosu i przekazać go komuś innemu, musisz zdjąć wszystkie pudła i przenieść pudełko na stos innej osoby (tylko stos)
  • Położyłeś wszystkie swoje pudełka (z wyjątkiem naprawdę małe pudełka) w specjalnym miejscu, w którym nie układasz rzeczy na wierzchu innych rzeczy i zapisuj, gdzie kładziesz je na kartce papieru (wskaźnik) i kładziesz papier na stosie. Jeśli chcesz przekazać skrzynkę komuś innemu, po prostu podaj mu kartkę ze stosu lub po prostu dobierz kserokopię papieru i zostaw oryginał na swoim stosie. (Stack + heap)

Te dwa przykłady są uogólnionymi uogólnieniami i są pewne punkty, które są rażąco błędne w analogii, ale są na tyle bliskie, że, mam nadzieję, pomogą ci dostrzec zalety w obu przypadkach.

+0

@MooingDuck Tak, ale pracujesz w pamięci wirtualnej w swoim programie, Jeśli wejdę w podprogram, umieść coś na stosie, a następnie powróć z podprogramu Będę musiał albo cofnąć alokację, albo przenieść obiekt, który utworzyłem, zanim będę mógł rozwiń stos, aby wrócić do miejsca, z którego przybyłem. –

+1

mimo że mój komentarz wynika z błędnej interpretacji (i usunąłem ją), nadal nie zgadzam się z tą odpowiedzią. Usunięcie 12 MB z góry stosu jest dosłownie jednym opchem. To w zasadzie za darmo. Również kompilatory mogą oszukiwać regułę "stosu", więc nie, nie muszą kopiować/przesuwać obiektu przed rozwinięciem, aby go zwrócić. Więc myślę, że twój komentarz jest również niepoprawny. –

+0

Cóż, zwykle nie ma większego znaczenia, że ​​zwolnienie 12 MB zajmuje jeden kod operacyjny na stosie powyżej 100 na stercie - jest to prawdopodobnie poniżej poziomu hałasu faktycznie przetwarzającego bufor 12 MB. Jeśli kompilatory chcą oszukiwać, gdy zauważą, że zwracany jest absurdalnie duży obiekt (np. Przesuwając SP przed wywołaniem, aby uczynić przestrzeń obiektu częścią stosu wywołującego), to jest w porządku, TBH, twórcy, którzy zwracają takie obiekty (a nie wskaźniki/punkty odniesienia) są w pewnym stopniu programowane. –

10

To tylko domyślny rozmiar. Jeśli potrzebujesz więcej, możesz uzyskać więcej - najczęściej mówiąc linkerowi, aby przydzielił dodatkowe miejsce na stosie.

Wadą posiadania dużych stosów jest to, że jeśli utworzysz wiele wątków, będą potrzebować po jednym stosie.Jeśli wszystkie stosy przydzielają wiele MB, ale nie używają go, przestrzeń zostanie zmarnowana.

Musisz znaleźć odpowiednie saldo swojego programu.


Niektórzy ludzie, jak @BJovke, wierzyć, że pamięć wirtualna jest zasadniczo wolny. Prawdą jest, że nie trzeba mieć fizycznej pamięci wspierającej całą pamięć wirtualną. Musisz przynajmniej podać adresy pamięci wirtualnej.

Jednak na typowym 32-bitowym komputerze PC rozmiar pamięci wirtualnej jest taki sam, jak rozmiar pamięci fizycznej - ponieważ mamy tylko 32 bity dla dowolnego adresu, wirtualnego lub nie.

Ponieważ wszystkie wątki w procesie współużytkują tę samą przestrzeń adresową, muszą ją podzielić między nie. Po tym, jak system operacyjny przejął swoją rolę, pozostało "tylko" 2-3 GB dla aplikacji. Ta wielkość jest limitem dla zarówno fizycznej, jak i wirtualnej, ponieważ nie ma już więcej adresów.

+0

Największym problemem z gwintowaniem jest to, że nie można łatwo sygnalizować obiektów stosu innym wątkom. Albo wątek producenta musi zsynchronizować się z oczekiwaniem, aż wątek konsumenta zwolni obiekt, albo trzeba wykonać kosztowne i głębokie kopie, które będą generowane przez rywalizację. –

+2

@MartinJames: Nikt nie mówi, że wszystkie obiekty powinny być na stosie, omawiamy, dlaczego domyślny rozmiar stosu jest mały. –

+0

Przestrzeń nie zostanie zmarnowana, rozmiar stosu jest po prostu rezerwą ciągłej wirtualnej przestrzeni adresowej.Jeśli więc ustawisz rozmiar stosu na 100 MB, ilość pamięci RAM, która ** zostanie ** wykorzystana, zależy od zużycia stosu w wątkach. – BJovke

-6

Nie sądzę, że istnieje jakiś techniczny powód, ale to byłaby dziwna aplikacja, która właśnie stworzyła jeden ogromny super-obiekt na stosie. Obiekty stosu nie mają elastyczności, która staje się bardziej problematyczna wraz ze wzrostem rozmiaru - nie można wrócić bez ich zniszczenia i nie można kolejkować ich do innych wątków.

+1

Nikt nie mówi, że wszystkie obiekty powinny znajdować się na stosie, dlatego mówimy o tym, dlaczego domyślny rozmiar stosu jest mały. –

+0

Nie jest mały! Ile połączeń funkcji trzeba wykonać, aby zużyć 1MB stosu? Wartości domyślne są i tak łatwo zmienione w linkerze, więc pozostaje nam pytanie "po co używać stosu zamiast sterty?" –

+2

jedno połączenie funkcyjne. 'int main() {bufor znaków [1048576]; } 'Jest to bardzo częsty problem dla początkujących. Oczywiście istnieje proste obejście problemu, ale dlaczego mielibyśmy potrzebować obejść rozmiar stosu? –

1

Pomyśl o stosie w kolejności zbliżonej do dalekiej. Rejestry są zbliżone do CPU (szybkie), stos jest trochę dalej (ale wciąż względnie blisko), a sterta jest daleko (wolny dostęp).

Stos znajduje się na stadzie, ale wciąż, ponieważ jest używany w sposób ciągły, prawdopodobnie nigdy nie opuszcza pamięci podręcznej procesora (CPU), dzięki czemu jest szybszy niż zwykły dostęp do sterty. Jest to powód, aby zachować rozsądnie duży rozmiar stosu; aby zachować jak najwięcej pamięci podręcznej. Przydzielanie dużych obiektów na stosie (prawdopodobnie automatycznie zmieniając rozmiar stosu w miarę przelewania się) jest sprzeczne z tą zasadą.

Jest to dobry paradygmat dla osiągów, a nie tylko z dawnych czasów.

+2

Podczas gdy uważam, że buforowanie odgrywa dużą rolę, ponieważ sztucznie zmniejsza wielkość stosu, muszę skorygować na wyciągu: "stos żyje na stercie". Zarówno stos, jak i sterty żyją w pamięci (wirtualnie lub fizycznie). –

12

Jednym z aspektów, że nikt jeszcze wymienić:

Ograniczona wielkość stosu jest wykrywanie błędów i mechanizm powstrzymywania.

Zasadniczo, głównym zadaniem stosu w C i C++ jest śledzenie stosu wywołań i zmiennych lokalnych, a jeśli stos wyrasta poza granice, prawie zawsze jest to błąd w projekcie i/lub zachowanie aplikacji.

Jeśli stos miałby rosnąć dowolnie duże, błędy te (jak nieskończona rekursja) zostałyby złapane bardzo późno, dopiero po wyczerpaniu zasobów systemu operacyjnego. Zapobiega temu ustawienie arbitralnego limitu rozmiaru stosu. Rzeczywisty rozmiar nie jest tak ważny, poza tym, że jest wystarczająco mały, aby zapobiec degradacji systemu.

0

Wiele rzeczy, które Twoim zdaniem wymagają dużego stosu, można zrobić w inny sposób.

"Algorytmy" Sedgewicka ma kilka dobrych przykładów "usuwania" rekurencji z algorytmów rekursywnych, takich jak QuickSort, przez zamianę rekursji na iterację. W rzeczywistości algorytm jest wciąż rekursywny i nadal istnieje stos, ale przydzielasz stos sortowania na stercie, zamiast używać stosu środowiska wykonawczego.

(I rzecz druga edycja, z algorytmami podanymi w Pascalu. Można go użył do ośmiu dolarów.)

Innym sposobem spojrzenia na nią, to czy uważasz, że potrzebujesz wielki stos, kod jest nieefektywny. Istnieje lepszy sposób, w którym zużywa się mniej żetonów.

Powiązane problemy