Raz wywołaj funkcję „DoStackOverflow” Twój kod i dostaniesz EStackOverflow błąd zgłoszony przez Delphi z komunikatem „przepełnienie stosu”.
funkcjonować DoStackOverflow: liczba całkowita;
zaczynać
wynik: = 1 + DoStackOverflow;
koniec;
Co to jest ten „stos” i dlaczego występuje przepełnienie przy użyciu powyższego kodu?
Tak więc funkcja DoStackOverflow rekurencyjnie wywołuje się - bez „strategii wyjścia” - po prostu obraca się i nigdy nie wychodzi.
Szybka poprawka, którą zrobiłbyś, to usunięcie oczywistego błędu, który masz, i upewnienie się, że funkcja istnieje w pewnym momencie (aby twój kod mógł kontynuować wykonywanie od miejsca, w którym wywołałeś funkcję).
Przechodzisz dalej i nigdy nie oglądasz się za siebie, nie dbając o błąd / wyjątek, ponieważ jest on teraz rozwiązany.
Pozostaje jednak pytanie: co to za stos i dlaczego występuje przepełnienie?
Pamięć w aplikacjach Delphi
Kiedy zaczynasz programować w Delphi, możesz napotkać błąd podobny do powyższego, możesz go rozwiązać i przejść dalej. Ten dotyczy alokacji pamięci. Przez większość czasu nie przejmowałbyś się alokacją pamięci tak długo jak ty
darmowe co tworzysz.Gdy zdobywasz więcej doświadczenia w Delphi, zaczynasz tworzyć własne klasy, tworzyć je, dbać o zarządzanie pamięcią i tym podobne.
Dojdziesz do punktu, w którym przeczytasz w Pomocy coś w rodzaju „Zmienne lokalne (zadeklarowane w ramach procedur i funkcji) znajdują się w aplikacji stos." i również Klasy są typami referencyjnymi, więc nie są kopiowane przy przypisaniu, są przekazywane przez referencje i są przydzielane w sterta.
Czym więc jest „stos”, a co „kupa”?
Stack vs. Sterta
Uruchamianie aplikacji w systemie Windows, w pamięci znajdują się trzy obszary, w których aplikacja przechowuje dane: pamięć globalna, sterta i stos.
Zmienne globalne (ich wartości / dane) są przechowywane w pamięci globalnej. Pamięć zmiennych globalnych jest zarezerwowana przez aplikację podczas uruchamiania programu i pozostaje przydzielona do momentu zakończenia programu. Pamięć zmiennych globalnych nazywana jest „segmentem danych”.
Ponieważ pamięć globalna jest przydzielana i zwalniana tylko raz po zakończeniu programu, nie obchodzi nas to w tym artykule.
Stos i sterta są tam, gdzie ma miejsce dynamiczna alokacja pamięci: kiedy tworzysz zmienną dla funkcji, gdy tworzysz instancję klasy, gdy wysyłasz parametry do funkcji i używasz / przekazujesz jej wynik wartość.
Co to jest stos?
Gdy deklarujesz zmienną wewnątrz funkcji, pamięć wymagana do przechowywania zmiennej jest przydzielana ze stosu. Po prostu piszesz „var x: integer”, używasz „x” w swojej funkcji, a kiedy funkcja kończy działanie, nie przejmujesz się alokacją pamięci ani zwolnieniem. Kiedy zmienna wykracza poza zakres (kod wychodzi z funkcji), pamięć zabrana na stos jest zwalniana.
Pamięć stosu jest alokowana dynamicznie przy użyciu podejścia LIFO („ostatnie weszło pierwsze wyszło”).
W Programy Delphi, pamięć stosu jest używana przez
- Lokalne zmienne rutynowe (metoda, procedura, funkcja).
- Rutynowe parametry i typy zwrotów.
- Funkcja Windows API połączenia.
- Rekordy (dlatego nie trzeba jawnie tworzyć instancji typu rekordu).
Nie musisz jawnie zwalniać pamięci na stosie, ponieważ pamięć jest przydzielana automatycznie dla ciebie, gdy na przykład deklarujesz zmienną lokalną dla funkcji. Po wyjściu z funkcji (czasem nawet wcześniej z powodu optymalizacji kompilatora Delphi) pamięć zmiennej zostanie automatycznie zwolniona.
Rozmiar stosu pamięci jest domyślnie wystarczająco duży dla twoich (tak skomplikowanych jak one) programów Delphi. Wartości „Maksymalny rozmiar stosu” i „Minimalny rozmiar stosu” w opcjach programu Linker dla twojego projektu określają wartości domyślne - w 99,99% nie trzeba tego zmieniać.
Pomyśl o stosie jak o stosie bloków pamięci. Kiedy deklarujesz / używasz zmiennej lokalnej, menedżer pamięci Delphi wybierze blok od góry, użyje go, a gdy nie będzie już potrzebny, zostanie zwrócony z powrotem na stos.
Po użyciu lokalnej pamięci zmiennych ze stosu, zmienne lokalne nie są inicjalizowane, gdy są deklarowane. Zadeklaruj zmienną „var x: integer” w jakiejś funkcji i po prostu spróbuj odczytać wartość po wejściu do funkcji - x będzie miało jakąś „dziwną” wartość niezerową. Dlatego zawsze należy inicjalizować (lub ustawiać wartość) zmienne lokalne przed odczytaniem ich wartości.
Dzięki LIFO operacje na stosie (alokacja pamięci) są szybkie, ponieważ do zarządzania stosem potrzeba tylko kilku operacji (push, pop).
Co to jest kupa?
Sterta to obszar pamięci, w którym przechowywana jest pamięć dynamicznie przydzielana. Podczas tworzenia instancji klasy pamięć jest przydzielana ze sterty.
W programach Delphi pamięć sterty jest używana przez / when
- Tworzenie instancji klasy.
- Tworzenie i zmiana rozmiaru tablic dynamicznych.
- Jawne przydzielanie pamięci za pomocą GetMem, FreeMem, New i Dispose ().
- Korzystanie z ciągów ANSI / wide / Unicode, wariantów, interfejsów (zarządzanych automatycznie przez Delphi).
Pamięć stert nie ma ładnego układu, w którym byłoby trochę porządku, alokując bloki pamięci. Kupa wygląda jak puszka marmuru. Alokacja pamięci ze sterty jest losowa, blok stąd, a nie blok stamtąd. Dlatego operacje na stosie są nieco wolniejsze niż na stosie.
Gdy poprosisz o nowy blok pamięci (tj. Utworzysz instancję klasy), menedżer pamięci Delphi zajmie się tym: otrzymasz nowy blok pamięci lub zużyty i odrzucony.
Sterta składa się z całej pamięci wirtualnej (Pamięć RAM i miejsce na dysku).
Ręczne przydzielanie pamięci
Teraz, gdy wszystko o pamięci jest jasne, możesz bezpiecznie (w większości przypadków) zignorować powyższe i po prostu kontynuować pisanie programów Delphi, tak jak robiłeś to wczoraj.
Oczywiście powinieneś wiedzieć, kiedy i jak ręcznie przydzielić / zwolnić pamięć.
Podniesiono „EStackOverflow” (od początku artykułu), ponieważ przy każdym wywołaniu DoStackOverflow z stosu wykorzystywany był nowy segment pamięci, a stos ma ograniczenia. Tak proste jak to.