Обадете се на функцията "DoStackOverflow" веднъж от вашия код и ще получите EStackOverflow грешка, повдигната от Delphi със съобщението "qaru overflow".
функция DoStackOverflow: цяло число;
започвам
резултат: = 1 + DoStackOverflow;
край;
Какъв е този "стек" и защо има преливане там с помощта на кода по-горе?
Така че, функцията DoStackOverflow рекурсивно се обажда - без „стратегия за излизане“ - тя просто продължава да се върти и никога не излиза.
Бързо поправяне, което бихте направили, е да изчистите очевидната грешка, която имате, и да се уверите, че функцията съществува в някакъв момент (така че вашият код да продължи да се изпълнява от мястото, където сте извикали функцията).
Продължавате напред и никога не поглеждате назад, без да се грижите за грешката / изключението, както сега е решено.
И все пак остава въпросът: какъв е този стек и защо има преливник?
Памет в вашите приложения на Delphi
Когато започнете да програмирате в Delphi, може да срещнете грешка като тази по-горе, да я разрешите и да продължите напред. Този е свързан с разпределението на паметта. През повечето време не бихте се интересували от разпределението на паметта, докато сте
безплатно това, което създавате.Като натрупате повече опит в Delphi, вие започвате да създавате свои собствени класове, да ги инстанцирате, да се грижите за управлението на паметта и други подобни.
Ще стигнете до момента, в който ще прочетете в Помощта нещо подобно „Локалните променливи (декларирани в рамките на процедури и функции) се намират в приложението купчина." и също Класовете са референтни типове, така че не се копират при задание, те се предават чрез препратка и се разпределят в купчина.
И така, какво е "стек" и какво е "грамада"?
Стек срещу купчина
Стартиране на приложението ви в Windows, има три области в паметта, където приложението ви съхранява данни: глобална памет, хеп и стек.
Глобалните променливи (техните стойности / данни) се съхраняват в глобалната памет. Паметта за глобални променливи се запазва от вашето приложение, когато програмата стартира и остава разпределена, докато програмата ви се прекрати. Паметта за глобални променливи се нарича "сегмент от данни".
Тъй като глобалната памет е само веднъж разпределена и освободена при прекратяване на програмата, в тази статия не ни интересува.
Стека и купчината са мястото, където се извършва динамично разпределение на паметта: когато създавате променлива за функция, когато създавате екземпляр от клас, когато изпращате параметри на функция и използвате / предавате нейния резултат стойност.
Какво е стек?
Когато декларирате променлива във функция, паметта, необходима за задържане на променливата, се разпределя от стека. Вие просто пишете "var x: integer", използвате "x" във вашата функция и когато функцията излезе, не ви пука за разпределение на паметта или освобождаване. Когато променливата излезе извън обхвата (кодът излиза от функцията), паметта, взета в стека, се освобождава.
Пакетната памет се разпределя динамично, като се използва подходът LIFO ("последен в първи изход").
в Програми на Delphi, стек паметта се използва от
- Локални рутинни (метод, процедура, функция) променливи.
- Рутинни параметри и видове връщане.
- Функция за API на Windows призовава.
- Записи (ето защо не е необходимо да създавате изрично екземпляр от тип запис).
Не е необходимо изрично да освобождавате паметта в стека, тъй като паметта автоматично се разпределя автоматично, когато например декларирате локална променлива за функция. Когато функцията излезе (понякога дори и преди поради оптимизация на компилатора на Delphi), паметта за променливата ще бъде автоматично магически освободена.
Размер на стека памет по подразбиране е достатъчно голям за вашите (толкова сложни, колкото са) програмите на Delphi. Стойностите „Максимален размер на стека“ и „Минимален размер на стека“ в опциите на Linker за вашия проект посочват стойности по подразбиране - в 99,99% няма да е необходимо да променяте това.
Мислете за стека като за купчина блокове памет. Когато декларирате / използвате локална променлива, мениджърът на паметта на Delphi ще избере блока от върха, ще го използва и когато вече не е необходим, ще бъде върнат обратно в стека.
Използвайки локална променлива памет от стека, локалните променливи не се инициализират, когато са декларирани. Декларирайте променлива "var x: integer" в някаква функция и просто опитайте да прочетете стойността, когато въведете функцията - x ще има някаква "странна" ненулева стойност. Така че, винаги инициализирайте (или задайте стойност) на вашите локални променливи, преди да прочетете тяхната стойност.
Поради LIFO операциите по стека (разпределение на паметта) са бързи, тъй като за управление на стека са необходими само няколко операции (push, pop).
Какво е Heap?
Купа е регион на паметта, в който се съхранява динамично разпределена памет. Когато създавате екземпляр от клас, паметта се разпределя от грамадата.
В програмите на Delphi, heap паметта се използва от / кога
- Създаване на екземпляр от клас.
- Създаване и оразмеряване на динамични масиви.
- Изрично разпределяне на паметта с помощта на GetMem, FreeMem, New и Dispose ().
- Използване на ANSI / wide / Unicode низове, варианти, интерфейси (управлявани автоматично от Delphi).
Heap паметта няма хубаво оформление, където би имало някакъв ред е разпределянето на блокове памет. Heap изглежда като кутия с мрамори. Разпределението на паметта от купчината е случайно, блок оттук, отколкото блок оттам. По този начин, операциите с купчина са малко по-бавни от тези в стека.
Когато поискате нов блок памет (т.е. създадете екземпляр от клас), Delphi мениджърът на памет ще се справи с това вместо вас: ще получите нов блок памет или използван и изхвърлен.
Купата се състои от цялата виртуална памет (RAM и дисково пространство).
Ръчно разпределяне на паметта
Сега, когато всичко за паметта е ясно, можете спокойно (в повечето случаи) да игнорирате горното и просто да продължите да пишете програми на Delphi, както направихте вчера.
Разбира се, трябва да сте наясно кога и как ръчно да разпределите / освободите памет.
"EStackOverflow" (от началото на статията) беше повдигнат, тъй като при всяко обаждане към DoStackOverflow се използва нов сегмент памет от стека и стека има ограничения. Толкова просто като това.