Razumevanje dodeljevanja pomnilnika v Delphiju
Getty Images/Daniel Sambraus
Enkrat pokličite funkcijo 'DoStackOverflow' iz vaša koda in dobil boš EStackOverflow napaka, ki jo sproži Delphi s sporočilom 'stack overflow'.
|_+_||_+_||_+_|
|_+_|
|_+_|
Kaj je ta 'sklad' in zakaj je tam prelivanje z uporabo zgornje kode?
Torej funkcija DoStackOverflow rekurzivno kliče samo sebe -- brez 'izhodne strategije' -- samo še naprej se vrti in nikoli ne zapusti.
Hitra rešitev, ki bi jo naredili, je, da počistite očitno napako, ki jo imate, in zagotovite, da funkcija na neki točki obstaja (tako da se lahko vaša koda nadaljuje z izvajanjem od tam, kjer ste poklicali funkcijo).
Greš naprej in se nikoli ne ozreš nazaj, pri čemer te ne zanima napaka/izjema, saj je zdaj odpravljena.
Kljub temu ostaja vprašanje: kaj je ta sklad in zakaj prihaja do prelivanja ?
Pomnilnik v vaših aplikacijah Delphi
Ko začnete programirati v Delphiju, lahko naletite na napako, kot je zgornja, rešili bi jo in nadaljevali. Ta je povezan z dodeljevanjem pomnilnika. Večino časa vam dodeljevanje pomnilnika ne bi bilo mar tako dolgo kot vi osvobodite, kar ustvarite .
Ko pridobite več izkušenj z Delphijem, začnete ustvarjati lastne razrede, jih instancirate, skrbite za upravljanje pomnilnika in podobno.
Prišli boste do točke, ko boste v pomoči prebrali nekaj takega 'Lokalne spremenljivke (deklarirane znotraj postopkov in funkcij) se nahajajo v aplikaciji kup .' in tudi Razredi so referenčni tipi, zato se ne kopirajo ob dodelitvi, prenesejo se po sklicu in so dodeljeni na kup .
Torej, kaj je 'sklad' in kaj 'kup'?
Stack vs. Heap
Zagon vaše aplikacije v sistemu Windows , obstajajo tri področja v pomnilniku, kjer vaša aplikacija shranjuje podatke: globalni pomnilnik, kopica in sklad.
Globalne spremenljivke (njihove vrednosti/podatki) so shranjene v globalnem pomnilniku. Pomnilnik za globalne spremenljivke rezervira vaša aplikacija, ko se program zažene, in ostane dodeljen, dokler se vaš program ne konča. Pomnilnik za globalne spremenljivke se imenuje 'segment podatkov'.
Ker se globalni pomnilnik dodeli in sprosti samo enkrat ob zaključku programa, nas v tem članku ne zanima.
Sklad in kopica sta tam, kjer poteka dinamično dodeljevanje pomnilnika: ko ustvarite spremenljivko za funkcijo, ko ustvarite primerek razreda, ko pošljete parametre funkciji in uporabite/posredujete njeno rezultatsko vrednost.
Kaj je Stack?
Ko deklarirate spremenljivko znotraj funkcije, se pomnilnik, potreben za shranjevanje spremenljivke, dodeli iz sklada. Preprosto napišete 'var x: integer', v svoji funkciji uporabite 'x' in ko funkcija zapre, vam ni mar za dodelitev ali sprostitev pomnilnika. Ko gre spremenljivka izven obsega (koda zapusti funkcijo), se pomnilnik, ki je bil zajet v skladu, sprosti.
Pomnilnik sklada se dodeljuje dinamično z uporabo pristopa LIFO ('zadnji vstop, prvi ven').
notri Delphi programi , pomnilnik sklada uporablja
- Spremenljivke lokalne rutine (metoda, postopek, funkcija).
- Rutinski parametri in vrste povratkov.
- Funkcija Windows APIklice.
- Zapisi (zato vam ni treba izrecno ustvariti primerka vrste zapisa).
Ni vam treba eksplicitno sprostiti pomnilnika v skladu, saj je pomnilnik samodejno magično dodeljen namesto vas, ko na primer izjavite lokalno spremenljivko za funkcijo. Ko funkcija zapre (včasih celo prej zaradi optimizacije prevajalnika Delphi), bo pomnilnik za spremenljivko samodejno magično osvobojen.
Velikost pomnilnika skladaje privzeto dovolj velik za vaše (ne glede na zapletenost) programe Delphi. Vrednosti »Največja velikost sklada« in »Najmanjša velikost sklada« v možnostih povezovalnika za vaš projekt določata privzete vrednosti – v 99,99 % vam tega ne bi bilo treba spreminjati.
Pomislite na sklad kot na kup pomnilniških blokov. Ko deklarirate/uporabite lokalno spremenljivko, bo upravljalnik pomnilnika Delphi izbral blok z vrha, ga uporabil in ko ne bo več potreben, bo vrnjen nazaj v sklad.
Ker je pomnilnik lokalnih spremenljivk uporabljen iz sklada, lokalne spremenljivke niso inicializirane, ko so deklarirane. V neki funkciji deklarirajte spremenljivko 'var x: integer' in samo poskusite prebrati vrednost, ko vnesete funkcijo -- x bo imel neko 'čudno' vrednost, ki ni nič. Zato vedno inicializirajte (ali nastavite vrednost) svojim lokalnim spremenljivkam, preden preberete njihovo vrednost.
Zaradi LIFO so operacije sklada (dodeljevanje pomnilnika) hitre, saj je za upravljanje sklada potrebnih le nekaj operacij (push, pop).
Kaj je Heap?
Kup je območje pomnilnika, v katerem je shranjen dinamično dodeljen pomnilnik. Ko ustvarite primerek razreda, se pomnilnik dodeli iz kopice.
V programih Delphi pomnilnik kopice uporablja/ko
- Ustvarjanje primerka razreda.
- Ustvarjanje in spreminjanje velikosti dinamičnih nizov.
- Izrecno dodeljevanje pomnilnika z uporabo GetMem, FreeMem, New in Dispose().
- Uporaba nizov ANSI/wide/Unicode, različic, vmesnikov (samodejno upravlja Delphi).
Pomnilnik kopice nima lepe postavitve, kjer bi bilo nekaj reda pri dodeljevanju blokov pomnilnika. Heap izgleda kot pločevinka frnikol. Dodeljevanje pomnilnika iz kopice je naključno, blok od tu in blok od tam. Tako so operacije kopice nekoliko počasnejše od tistih na skladu.
Ko zahtevate nov pomnilniški blok (tj. ustvarite primerek razreda), bo upravitelj pomnilnika Delphi poskrbel za to namesto vas: dobili boste nov pomnilniški blok ali uporabljenega in zavrženega.
Kopica je sestavljena iz celotnega navideznega pomnilnika ( RAM in prostor na disku ).
Ročno dodeljevanje pomnilnika
Zdaj, ko je vse o pomnilniku jasno, lahko varno (v večini primerov) zgoraj ignorirate in preprosto nadaljujete s pisanjem programov Delphi, kot ste počeli včeraj.
Seveda se morate zavedati, kdaj in kako ročno dodeliti/sprostiti pomnilnik.
'EStackOverflow' (z začetka članka) je bil postavljen, ker je bil z vsakim klicem DoStackOverflow uporabljen nov segment pomnilnika iz sklada in sklad ima omejitve. Tako preprosto.