it-swarm-eu.dev

Co a kde je hromada a hromada?

Knihovny programovacího jazyka vysvětlují, že typy hodnot jsou vytvořeny na stack , a referenční typy jsou vytvořeny na haldě , bez vysvětlení, co tyto dvě věci jsou. Nečetl jsem to jasné vysvětlení. Chápu, co je stack. Ale, 

  • kde a co jsou (fyzicky v reálné paměti počítače)?
  • Do jaké míry jsou řízeny operačním systémem nebo jazykovým runtime?
  • Jaký je jejich rozsah?
  • Co určuje velikost každého z nich?
  • Co dělá jeden rychlejší? 
7473
mattshane

Zásobník je paměť odložená jako poškrábaný prostor pro provedení provádění. Když je volána funkce, blok je rezervován v horní části zásobníku pro lokální proměnné a některá data účetnictví. Když se tato funkce vrátí, blok se nepoužije a může být použit při příštím volání funkce. Zásobník je vždy rezervován v pořadí LIFO (poslední v prvním ven); poslední rezervovaný blok je vždy další blok, který má být uvolněn. Díky tomu je velmi jednoduché sledovat sklad. uvolnění bloku ze zásobníku není nic jiného než nastavení jednoho ukazatele.

Hromada je vyhrazena pro dynamickou alokaci. Na rozdíl od stacku neexistuje žádný vynucený vzor pro alokaci a uvolnění bloků z haldy; kdykoli můžete přidělit blok a uvolnit jej. Díky tomu je mnohem složitější sledovat, které části haldy jsou v daném čase přiděleny nebo uvolněny; existuje mnoho vlastních alokátorů haldy k dispozici pro vyladění výkonu haldy pro různé způsoby použití.

Každé vlákno dostane zásobník, zatímco pro aplikaci je obvykle pouze jedna halda (ačkoli není neobvyklé mít více hromad pro různé typy přidělení).

Přímé odpovědi na vaše dotazy: 

Do jaké míry jsou ovládány operačním systémem nebo jazykovým modulem runtime?

Operační systém přidělí zásobník pro každý podproces na úrovni systému při vytvoření podprocesu. Obvykle je OS volán jazykem runtime přidělit haldy pro aplikaci.

Jaký je jejich rozsah?

Zásobník je připojen k podprocesu, takže při ukončení podprocesu je zásobník obnoven. Halda je obvykle přidělena při spuštění aplikace runtime a je obnovena, když aplikace (technicky zpracovává) skončí.

Co určuje velikost každého z nich? 

Velikost zásobníku je nastavena při vytvoření podprocesu. Velikost haldy je nastavena na spouštění aplikace, ale může roste, jak je potřeba prostor (alokátor požaduje více paměti z operačního systému).

Co je rychlejší?

Zásobník je rychlejší, protože přístupový vzor z něj činí triviální přidělení a uvolnění paměti z něj (ukazatel/celé číslo je jednoduše zvýšen nebo snížen), zatímco halda má mnohem složitější účetnictví, které se podílí na přidělení nebo uvolnění. Také každý bajt v zásobníku má tendenci být opakovaně používán velmi často, což znamená, že má tendenci být mapován do mezipaměti procesoru, což je velmi rychlé. Dalším zásahem do haldy je to, že halda, která je většinou globálním zdrojem, musí být zpravidla vícevláknová bezpečná, tj. Každé přidělení a rozmístění musí být - typicky - synchronizováno s "všemi" jinými přístupy haldy v programu.

Jasná demonstrace:
Zdroj obrázku: vikashazrati.wordpress.com

5529
Jeff Hill

Zásobník:

  • Uloženo v počítači RAM stejně jako halda.
  • Proměnné vytvořené v zásobníku se dostanou mimo rozsah a automaticky se uvolní.
  • Mnohem rychleji přidělit ve srovnání s proměnnými na haldě.
  • Implementováno se skutečnou strukturou dat zásobníku.
  • Ukládá lokální data, návratové adresy, používané pro předávání parametrů.
  • Může mít přetečení zásobníku, když se použije příliš mnoho zásobníku (většinou z nekonečné nebo příliš hluboké rekurze, velmi velké alokace).
  • Data vytvořená v zásobníku lze použít bez ukazatelů.
  • Zásobník byste použili, pokud přesně víte, kolik dat potřebujete k přidělení před kompilací a není příliš velká.
  • Obvykle má maximální velikost již určen při spuštění programu.

Haldy:

  • Uloženo v počítači RAM stejně jako zásobník.
  • V C++ musí být proměnné na haldě ručně zničeny a nikdy nevypadnou z rozsahu. Data jsou uvolněna pomocí delete, delete[] nebo free.
  • Pomalejší přidělení ve srovnání s proměnnými v zásobníku.
  • Používá se na vyžádání k přidělení bloku dat pro použití programem.
  • Může mít roztříštěnost, pokud existuje spousta alokací.
  • V C++ nebo C budou data vytvořená na haldě označena ukazateli a přidělena pomocí new nebo malloc resp.
  • Může mít selhání přidělení, pokud je požadováno přidělení příliš velké vyrovnávací paměti.
  • Haldu byste použili, pokud přesně nevíte, kolik dat budete potřebovat v době běhu nebo pokud potřebujete přidělit spoustu dat.
  • Zodpovědný za nevracení paměti.

Příklad:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;
2199
Brian R. Bondy

Nejdůležitějším bodem je to, že halda a zásobník jsou obecné termíny pro způsoby, kterými lze paměť přidělit. Mohou být implementovány mnoha různými způsoby a termíny se vztahují na základní pojmy.

  • V hromádce položek, položky sedí jeden nahoře jiný v pořadí oni byli umístil tam, a vy můžete jen odstranit ten horní (bez překlopení celá věc přes).

    Stack like a stack of papers

    Jednoduchost zásobníku je v tom, že nemusíte udržovat tabulku obsahující záznam každé sekce přidělené paměti; Jediné informace o stavu, které potřebujete, jsou jeden ukazatel na konec zásobníku. Chcete-li přidělit a rozdělit, stačí pouze zvýšit a snížit tento jednotlivý ukazatel. Poznámka: zásobník může být někdy implementován tak, aby začínal v horní části části paměti a rozšiřoval se směrem dolů, spíše než aby rostl nahoru.

  • V haldě není žádné zvláštní pořadí, ve kterém jsou položky umístěny. Můžete se dostat do a odstranit položky v libovolném pořadí, protože neexistuje žádná jasná položka 'top'.

    Heap like a heap of licorice allsorts

    Přidělení haldy vyžaduje udržování úplného záznamu o tom, jaká paměť je přidělena a co ne, stejně jako některé režijní náklady na snížení fragmentace, nalezení souvislých paměťových segmentů dostatečně velkých, aby se vešly na požadovanou velikost, a tak dále. Paměť může být uvolněna kdykoliv a ponechá volné místo. Někdy bude alokátor paměti provádět úlohy údržby, například defragmentaci paměti přesunutím přidělené paměti, nebo shromažďováním odpadků, které identifikuje v době běhu, když paměť již není v rozsahu a uvolňuje ji. 

Tyto obrazy by měly dělat docela dobrou práci popisující dva způsoby alokace a uvolnění paměti v zásobníku a haldy. Mňam!

  • Do jaké míry jsou řízeny operačním systémem nebo jazykovým runtime?

    Jak bylo zmíněno, halda a zásobník jsou obecné termíny a mohou být implementovány mnoha způsoby. Počítačové programy mají obvykle svazek nazvaný zásobník volání který ukládá informace relevantní pro aktuální funkci, jako je ukazatel na jakoukoli funkci, ze které byla vyvolána, a všechny místní proměnné. Protože funkce volají další funkce a pak se vrátí, zásobník roste a zmenšuje se, aby držel informace z funkcí dále v zásobníku volání. Program ve skutečnosti nemá nad ním kontrolu runtime; je určeno programovacím jazykem, OS a dokonce i architekturou systému.

    Hromada je obecný termín používaný pro všechny paměti, které jsou přidělovány dynamicky a náhodně; tj. mimo provoz. Paměť je obvykle přidělena operačním systémem, přičemž k tomuto přidělení slouží funkce API pro volání aplikací. Pro správu dynamicky přidělené paměti, která je obvykle zpracovávána operačním systémem, je zapotřebí spravedlivý kousek režie.

  • Jaký je jejich rozsah?

    Zásobník volání je takový koncept nízké úrovně, že se nevztahuje na 'rozsah' ve smyslu programování. Pokud rozložíte nějaký kód, uvidíte relativní odkazy na styl ukazatele na části zásobníku, ale pokud jde o jazyk vyšší úrovně, jazyk ukládá svá vlastní pravidla rozsahu. Jedním z důležitých aspektů zásobníku je však to, že jakmile se funkce vrátí, cokoliv z této funkce se okamžitě uvolní ze zásobníku. To funguje tak, jak byste očekávali, že bude fungovat, jak budou vaše programovací jazyky fungovat. V haldě je také těžké definovat. Rozsah je cokoliv, co je vystaveno OS, ale váš programovací jazyk pravděpodobně přidává svá pravidla o tom, co je ve vaší aplikaci „rozsah“. Architektura procesoru a operační systém používají virtuální adresování, které procesor převádí na fyzické adresy a jsou zde chyby stránek atd. Sledují, které stránky patří k aplikacím. Nikdy se o to nemusíte obávat, protože používáte jakoukoli metodu, kterou váš programovací jazyk používá k přidělení a uvolnění paměti, a zkontrolujte chyby (pokud z nějakého důvodu selže přidělení/uvolnění).

  • Co určuje velikost každého z nich?

    Znovu záleží na jazyce, kompilátoru, operačním systému a architektuře. Zásobník je obvykle předem přidělen, protože podle definice musí být souvislá paměť (více o tom v posledním odstavci). Jeho velikost určuje překladač jazyků nebo operační systém. Neuchováváte velké množství dat na zásobníku, takže bude dostatečně velká, aby se nikdy nepoužila, s výjimkou případů nežádoucí nekonečné rekurze (tedy "přetečení zásobníku") nebo jiných neobvyklých programovacích rozhodnutí.

    Hromada je obecný termín pro cokoliv, co lze dynamicky přidělit. V závislosti na tom, jak se na to díváte, se neustále mění velikost. V moderních procesorech a operačních systémech je ale stejně tak velmi přesný způsob, jakým to funguje, takže se obvykle nemusíte starat o to, jak to funguje, kromě toho, že (v jazycích, kde vám to umožňuje) nesmíte používat paměť, ještě jste nepřidělili paměť, kterou jste uvolnili.

  • Zásobník je rychlejší, protože všechny volné paměti jsou vždy souvislé. Není třeba udržovat seznam všech segmentů volné paměti, pouze jeden ukazatel na aktuální vrchol zásobníku. Kompilátory obvykle ukládají tento ukazatel do speciálního, rychlého registru pro tento účel. A co víc, následné operace ve svazku jsou obvykle soustředěny do velmi blízkých oblastí paměti, což je na velmi nízké úrovni vhodné pro optimalizaci procesorem na procesorech.

    .

1305
thomasrutter

V následujícím kódu C #

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

Zde je popsáno, jak je paměť spravována

Picture of variables on the stack

Local Variables, které musí trvat pouze tak dlouho, dokud funkce vyvolá v zásobníku. Hromada se používá pro proměnné, jejichž životnost víme až dopředu, ale očekáváme, že vydrží chvíli. Ve většině jazyků je kritické, že v době kompilace víme, jak velká je proměnná, pokud ji chceme uložit do zásobníku. 

Objekty (které se liší velikostí, jak je aktualizujeme) jdou na haldy, protože v době vytvoření nevíme, jak dlouho budou trvat. V mnoha jazycích je halda shromážděna k odstranění objektů (například objektu cls1), které již nemají žádné odkazy. 

V jazyce Java většina objektů přechází přímo do haldy. V jazycích, jako je C/C++, mohou struktury a třídy často zůstat v zásobníku, když se nezabýváte ukazateli.

Více informací naleznete zde:

Rozdíl mezi alokací zásobníku a haldy «timmurphy.org

a tady: 

Vytváření objektů na zásobníku a haldě

Tento článek je zdrojem obrázku nahoře: Šest důležitých .NET konceptů: Stack, halda, typy hodnot, referenční typy, box a unboxing - CodeProject

ale uvědomte si, že může obsahovat určité nepřesnosti. 

379
Snowcrash

Stack Když zavoláte funkci, argumenty této funkce a některé další režie se vloží do zásobníku. Tam jsou také uloženy některé informace (např. Kam jít po návratu). Když deklarujete proměnnou uvnitř vaší funkce, tato proměnná je také přidělena v zásobníku. 

Deallocating stack je velmi jednoduchý, protože vždy vyděláváte v opačném pořadí, ve kterém přidělujete. Stack věci se přidávají při zadávání funkcí, odpovídající data jsou odstraněna při ukončení. To znamená, že máte tendenci zůstat v malé oblasti zásobníku, pokud nevolat mnoho funkcí, které volají spoustu jiných funkcí (nebo vytvořit rekurzivní řešení).

Heap Hromada je obecný název, kam umístíte data, která vytvoříte za běhu. Pokud nevíte, kolik vesmírných lodí váš program vytvoří, budete pravděpodobně používat nový (nebo malloc nebo ekvivalentní) operátor k vytvoření každé kosmické lodi. Toto přidělení se na chvíli bude držet, takže je pravděpodobné, že věci uvolníme v jiném pořadí, než jsme je vytvořili. 

Tak, halda je mnohem složitější, protože tam skončí oblasti paměti, které jsou nepoužívány prokládané s kousky, které jsou - fragmentace paměti. Nalezení volné paměti velikosti, kterou potřebujete, je obtížný problém. To je důvod, proč by se halda měla vyhnout (i když je stále často používána).

Implementace Implementace zásobníku i haldy je obvykle na runtime/OS. Hry a jiné aplikace, které jsou kritické z hlediska výkonu, často vytvářejí vlastní řešení paměti, která zachytí velký kus paměti z haldy a poté ji vymyjí interně, aby se zabránilo spoléhání se na operační systém pro paměť. 

To je praktické pouze v případě, že vaše paměť je zcela odlišná od normy - tj. Pro hry, kde nahrajete úroveň v jedné obrovské operaci a můžete celou partii skrýt v další obrovské operaci.

Fyzické umístění v paměti Toto je méně důležité, než si myslíte, protože se jedná o technologii nazvanou Virtuální paměť která činí váš program přesvědčeným, že máte přístup k určité adrese, kde jsou fyzická data někde (i na pevném disku!). Adresy, které získáte pro zásobník, jsou v rostoucím pořadí, jak se váš strom volání dostane hlouběji. Adresy haldy jsou nepředvídatelné (tj. Implimentační specifické) a upřímně řečeno, není důležité.

194
Tom Leys

Chcete-li objasnit, tato odpověď má nesprávné informace ( thomas opravil jeho odpověď po komentářích, cool :)). Další odpovědi se vyhnou vysvětlení, co znamená statická alokace. Vysvětlím tedy tři hlavní formy alokace a jak se obvykle týkají segmentu haldy, zásobníku a dat níže. Ukážu také několik příkladů jak v C/C++, tak v Pythonu, abych pomohl lidem porozumět.

"Statické" (AKA staticky přidělené) proměnné nejsou přiřazeny k zásobníku. Nepředpokládejte tak - mnoho lidí dělá jen proto, že "statické" zní hodně jako "stack". Ve skutečnosti neexistují ani v zásobníku ani v haldě. Jsou součástí toho, co se nazývá datový segment .

Obecně je však lepší zvážit spíše "scope" a "lifetime" než "stack" a "haldy".

Rozsah se týká toho, které části kódu mají přístup k proměnné. Obecně si myslíme, že local scope (lze přistupovat pouze aktuální funkcí) versus global scope (lze přistupovat kdekoli), i když rozsah může být mnohem složitější.

Životnost označuje, kdy je proměnná přidělena a uvolněna během provádění programu. Obvykle si myslíme, že statická alokace (proměnná bude trvat po celou dobu trvání programu, což je užitečné pro ukládání stejných informací napříč několika funkčními voláními) versus automatické přidělení (proměnná trvá pouze během jednoho hovoru k funkci, která je užitečná pro ukládání informací, které se používají pouze během vaší funkce, a lze je zahodit, jakmile budete hotovi) versus dynamické přidělení (proměnné, jejichž doba trvání je definována v době běhu, místo času kompilace jako statické nebo automatické ).

Ačkoli většina kompilátorů a tlumočníků implementuje toto chování podobně, pokud jde o použití zásobníků, hromad, atd., Kompilátor může někdy tyto konvence porušit, pokud chce, pokud je chování správné. Například z důvodu optimalizace může lokální proměnná existovat pouze v registru nebo být zcela odstraněna, i když většina lokálních proměnných existuje v zásobníku. Jak bylo zdůrazněno v několika poznámkách, můžete volně implementovat kompilátor, který ani nepoužívá zásobník nebo haldu, ale místo toho některé další mechanismy ukládání (zřídka provedené, protože komíny a hromady jsou na to skvělé).

Budu poskytovat nějaký jednoduchý anotovaný C kód pro ilustraci tohoto všeho. Nejlepší způsob, jak se naučit, je spustit program v ladicím programu a sledovat chování. Pokud chcete číst python, přeskočte na konec odpovědi :)

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

Zvláště důvtipným příkladem, proč je důležité rozlišovat mezi životností a rozsahem, je to, že proměnná může mít lokální rozsah, ale statickou životnost - například "someLocalStaticVariable" ve vzorku kódu výše. Takovéto proměnné mohou učinit naše společné, ale neformální zvyklosti jmenování velmi matoucími. Například když řekneme "lokální", obvykle se rozumí "lokálně zmenšená automaticky přidělená proměnná" a když řekneme globální, obvykle se rozumí "globálně odstupňovaná staticky přidělená proměnná". Bohužel, pokud jde o věci, jako je "soubor rozsah staticky přidělené proměnné" mnoho lidí jen říct ... "huh ???".

Některé z možností syntaxe v C/C++ tento problém zhoršují - například mnoho lidí si myslí, že globální proměnné nejsou "statické" kvůli syntaxi uvedené níže.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

Všimněte si, že vložení klíčového slova "statické" do výše uvedeného deklarace zabrání tomu, aby měl var2 globální rozsah. Globální var1 však má statickou alokaci. To není intuitivní! Z tohoto důvodu se snažím nikdy nepoužívat "statické" slovo při popisu rozsahu a místo toho říkat něco jako "soubor" nebo "soubor omezený" rozsah. Mnoho lidí však používá výraz "statický" nebo "statický rozsah" k popisu proměnné, ke které lze přistupovat pouze z jednoho souboru kódu. V kontextu životnosti, "statické" vždy znamená, že proměnná je přidělena při spuštění programu a uvolněna při ukončení programu.

Někteří lidé si myslí, že tyto pojmy jsou specifické pro C/C++. Nejsou. Níže uvedený příklad Pythonu ilustruje všechny tři typy alokací (v tlumočených jazycích jsou možné některé nepatrné rozdíly, do kterých se nedostanu).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __== "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones
174
davec

Jiní odpověděli na široké tahy docela dobře, takže hodím pár detailů.

  1. Stack a halda nemusí být jednotné. Běžná situace, ve které máte více než jeden zásobník, je, pokud máte v procesu více vláken. V tomto případě má každý podproces svůj vlastní zásobník. Můžete také mít více než jednu haldu, například některé konfigurace DLL mohou mít za následek přidělení různých knihoven DLL z různých hromad, což je důvod, proč je obecně špatné uvolnit paměť přidělenou jinou knihovnou.

  2. V C můžete získat výhodu variabilní délky přidělení pomocí alloca , která přiděluje na zásobníku, na rozdíl od alloc, který přiděluje na haldě. Tato paměť nepřežije vaše prohlášení o návratu, ale je to užitečné pro vyrovnávací paměť.

  3. Vytvoření obrovského dočasného vyrovnávací paměti v systému Windows, který nepoužíváte, není zcela zdarma. Je to proto, že kompilátor bude generovat smyčku sondy zásobníku, která je volána při každém zadání funkce, aby se ujistil, že zásobník existuje (protože systém Windows používá na konci zásobníku jednu stránku ochrany, aby zjistil, kdy potřebuje růst zásobníku. Pokud přistupujete k paměti více než jednu stránku z konce zásobníku, který se zhroutí). Příklad:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}
158
Don Neufeld

Jiní přímo odpověděli na vaši otázku, ale když se snažím porozumět zásobníku a haldě, myslím, že je užitečné zvážit rozložení paměti tradičního procesu UNIX (bez podprocesů a mmap()- alokátorů). Slovníček Správa paměti webová stránka obsahuje schéma tohoto rozložení paměti.

Zásobník a halda jsou tradičně umístěny na opačných koncích virtuálního adresového prostoru procesu. Zásobník roste automaticky při přístupu, až do velikosti nastavené jádrem (které lze upravit pomocí setrlimit(RLIMIT_STACK, ...)). Halda roste, když alokátor paměti vyvolá systémové volání brk() nebo sbrk(), mapující více stránek fyzické paměti do virtuálního adresového prostoru procesu. 

V systémech bez virtuální paměti, jako jsou některé vestavěné systémy, se často používá stejné základní rozvržení, s výjimkou zásobníku a haldy, které mají pevnou velikost. V jiných vestavěných systémech (jako jsou systémy založené na mikroprocesorech Microchip PIC) je však programový zásobník samostatným blokem paměti, který není adresovatelný instrukcemi pro pohyb dat, a lze jej modifikovat nebo číst pouze nepřímo prostřednictvím instrukcí toku programů (volání, návratnosti atd.). Ostatní architektury, jako jsou procesory Intel Itanium, mají více svazků . V tomto smyslu je zásobník prvkem architektury CPU.

127
bk1e

Zásobník je část paměti, se kterou lze manipulovat pomocí několika klíčových instrukcí jazyka sestavy, například „pop“ (odebrat a vrátit hodnotu ze zásobníku) a „Push“ (stisknout hodnotu do zásobníku), ale také zavolat ( zavolání podprogramu - toto posune adresu zpět do zásobníku a vrátí se (vrátí se z podprogramu - tím se vypne adresa zásobníku a skočí na něj). Je to oblast paměti pod registrem ukazatele zásobníku, který lze nastavit podle potřeby. Zásobník se také používá pro předávání argumentů podprogramům a také pro uchování hodnot v registrech před voláním podprogramů.

Halda je část paměti, která je dána do aplikace operačním systémem, typicky prostřednictvím syscall jako malloc. Na moderních operačních systémech je tato paměť souborem stránek, ke kterým má přístup pouze volající proces.

Velikost zásobníku je určena za běhu a po spuštění programu obecně neroste. V programu C musí být zásobník dostatečně velký, aby udržel každou proměnnou deklarovanou v rámci každé funkce. Halda bude podle potřeby růst dynamicky, ale operační systém nakonec provede volání (bude často růst haldu o více než hodnotu, kterou požaduje malloc, takže alespoň někteří budoucí malloci nebudou muset vrátit zpět k jádru na toto chování je často přizpůsobitelné)

Vzhledem k tomu, že jste před spuštěním programu přidělili zásobník, nemusíte ho dříve, než budete moci použít zásobník, tak to je malá výhoda. V praxi je velmi těžké předvídat, co bude rychlé a co bude pomalé v moderních operačních systémech, které mají podsystémy virtuální paměti, protože jak jsou stránky implementovány a kde jsou uloženy, je detail implementace. 

108
Daniel Papasian

Myslím, že mnoho dalších lidí vám v této věci poskytlo většinou správné odpovědi.

Jeden detail, který byl vynechán, nicméně je to “halda” by měl ve skutečnosti pravděpodobně být volán “volný obchod”. Důvodem tohoto rozdílu je, že původní volný obchod byl implementován s datovou strukturou známou jako „binomická halda“. Z tohoto důvodu alokace z časných implementací malloc ()/free () byla přidělena z haldy. Nicméně, v tomto moderním dni, většina volných obchodů je realizováno s velmi komplikovanými datovými strukturami, které nejsou binomické hromady.

107
Heath

Co je to zásobník?

Stoh je hromada objektů, typicky to je úhledně uspořádané.

 Enter image description here

Hromádky ve výpočetních architekturách jsou oblasti paměti, do kterých se přidávají nebo odstraňují data posledním způsobem. 
V aplikaci s více podprocesy bude mít každý podproces svůj vlastní zásobník.

Co je to halda?

Hromada je neuspořádaná sbírka věcí, která se hromadí nahodile.

 Enter image description here

V počítačových architekturách je halda oblastí dynamicky přidělené paměti, která je řízena automaticky operačním systémem nebo knihovnou správce paměti. 
Paměť na haldě je přidělena, uvolněna a pravidelně měněna během provádění programu, což může vést k problému nazývanému fragmentace. 
Fragmentace nastane, když jsou objekty paměti přidělené malými mezerami mezi nimi, které jsou příliš malé pro uložení dalších paměťových objektů. 
Čistý výsledek je procento prostoru haldy, který není použitelný pro další přidělení paměti.

Oba společně

V aplikaci s více podprocesy bude mít každý podproces svůj vlastní zásobník. Ale všechny různé podprocesy budou sdílet haldu. 
Protože různé podprocesy sdílejí haldu v aplikaci s více podprocesy, znamená to také, že musí existovat nějaká koordinace mezi vlákny, aby se nepokoušely o přístup a manipulaci se stejným kusem (kousky). paměti v haldě.

Co je rychlejší - zásobník nebo halda? A proč?

Zásobník je mnohem rychlejší než halda. 
Důvodem je způsob přidělení paměti v zásobníku. 
Přidělování paměti do zásobníku je stejně jednoduché jako přesunutí ukazatele zásobníku nahoru.

Pro lidi nové programování je asi dobrý nápad použít zásobník, protože je to jednodušší. 
Protože zásobník je malý, budete ho chtít použít, když přesně víte, kolik paměti budete potřebovat pro vaše data, nebo pokud víte, že velikost vašich dat je velmi malá. 
Je lepší použít haldu, když víte, že budete potřebovat spoustu paměti pro vaše data, nebo si nejste jisti, kolik paměti budete potřebovat (jako u dynamického pole).

Model paměti Java

 Enter image description here

Zásobník je oblast paměti, kde jsou uloženy lokální proměnné (včetně parametrů metody). Pokud jde o proměnné objektu, jedná se pouze o odkazy (ukazatele) na skutečné objekty v haldě.
Pokaždé, když je objekt vytvořen instancí, je část paměti haldy odložena tak, aby uchovávala data (stav) daného objektu. Protože objekty mohou obsahovat jiné objekty, některá z těchto dat mohou ve skutečnosti obsahovat odkazy na tyto vnořené objekty.

101
Shreyos Adikari

S komínem můžete udělat nějaké zajímavé věci. Například, máte funkce jako alloca (za předpokladu, že můžete dostat přes hojné varování týkající se jeho použití), což je forma malloc, která konkrétně používá zásobník, ne haldy, pro paměť.

To znamená, že chyby v paměti založené na zásobníku jsou jedny z nejhorších, které jsem zažil. Pokud používáte paměť haldy a překročíte hranice přiděleného bloku, máte slušnou šanci spustit chybu segmentu. (Ne 100%: váš blok může být náhodně souvislý s jiným, který jste dříve přidělili.) Protože ale proměnné vytvořené v zásobníku jsou vždy vzájemně souvislé, zápis z mezí může změnit hodnotu jiné proměnné. Naučil jsem se, že kdykoliv mám pocit, že můj program přestal dodržovat logické zákony, je to pravděpodobně přetečení vyrovnávací paměti.

87
Peter

Jednoduše řečeno, zásobník je místo, kde se vytvářejí lokální proměnné. Také pokaždé, když zavoláte podprogram, čítač programu (ukazatel na další instrukci stroje) a všechny důležité registry, a někdy se parametry dostanou na zásobník. Pak jsou všechny lokální proměnné uvnitř podprogramu posunuty do zásobníku (a odtud použity). Když skončí podprogram, všechny věci se vyskakují ze zásobníku. Data z PC a registru se dostanou a vrátí tam, kde to bylo, jak je vyskakované, takže váš program může jít na své veselé cestě.

Hromada je oblast přidělení paměti dynamické paměti jsou provedeny z (explicitní "nové" nebo "přidělit" volání). Jedná se o speciální datovou strukturu, která umožňuje sledování bloků paměti různých velikostí a jejich alokačního stavu.

V "klasických" systémech RAM byl rozložen tak, že ukazatel zásobníku začínal v dolní části paměti, ukazatel haldy začínal nahoře a rostli k sobě. Pokud se překrývají, jste mimo RAM. To však nefunguje s moderními vícevláknovými OS. Každé vlákno musí mít svůj vlastní zásobník a ty se mohou vytvořit dynamicky.

84
T.E.D.

Od WikiAnwser.

Zásobník

Když funkce nebo metoda volá jinou funkci, která zase volá jinou funkci atd., Provedení všech těchto funkcí zůstane pozastaveno, dokud poslední funkce nevrátí svou hodnotu.

Tento řetězec zavolání funkce volání je zásobník, protože prvky v zásobníku (funkce volání) závisí na sobě.

Zásobník je důležité vzít v úvahu při zpracování výjimek a provádění podprocesů.

Halda

Halda je prostě paměť, kterou používají programy k ukládání proměnných.

78
devXen

Zásobník

  • Velmi rychlý přístup
  • Nemusíte explicitně de-allocate proměnné
  • Prostor je řízen efektivně CPU, paměť se nestane fragmentovaná
  • Pouze lokální proměnné
  • Limit velikosti zásobníku (závislý na OS)
  • Proměnné nelze měnit

Heap

  • Proměnné lze přistupovat globálně
  • Bez omezení velikosti paměti
  • (Poměrně) pomalejší přístup
  • Žádné zaručené efektivní využití prostoru, paměť může být časem fragmentována, protože bloky paměti jsou přiděleny, pak uvolněny
  • Musíte spravovat paměť (máte na starosti přidělování a uvolňování proměnných)
  • Proměnné lze měnit pomocí realloc ()
50
unknown

OK, jednoduše a krátkými slovy, znamenají objednaný a není objednán ...!

Stack: V položkách zásobníku se věci dostanou na vrchol každého druhého, což znamená, že bude rychlejší a efektivnější být zpracován! 

Takže vždy existuje index, který ukazuje na konkrétní položku, také zpracování bude rychlejší, existuje také vztah mezi položkami! ...

Heap: Žádná objednávka, zpracování bude pomalejší a hodnoty se zkomplikují spolu s žádnou specifickou objednávkou nebo indexem ... tam jsou náhodné a neexistuje žádný vztah mezi nimi ... takže provedení a doba použití by se mohla lišit. ..

Také vytvořím níže uvedený obrázek, který ukazuje, jak mohou vypadat:

 enter image description here

38
Alireza

V osmdesátých letech se UNIX šířil jako zajíčci s velkými společnostmi, které se valily vlastními silami. Exxon měl jednu stejně jako desítky značek ztracených v historii. realizátorů.

Typický program C byl uložen v paměti s možností Zvýšit hodnotu změnou hodnoty brk (). Typicky byl HEAP těsně pod touto hodnotou brk A vzrůstající brk zvýšil množství dostupné haldy.

Jediný STACK byl typicky oblast pod HEAP, což byl trakt paměti Obsahující nic hodnotného až do vrcholu dalšího pevného bloku paměti. Tento další blok byl často CODE, který mohl být přepsán podle dat zásobníku v jednom ze slavných hacků své doby.

Jeden typický paměťový blok byl BSS (blok nulových hodnot) , Který nebyl náhodně vynulován v nabídce jednoho výrobce. Další bylo DATA obsahující inicializované hodnoty, včetně řetězců a čísel. Třetí byl CODE obsahující CRT (C runtime), hlavní, funkce a knihovny.

Příchod virtuální paměti v systému UNIX mění mnoho omezení Neexistuje žádný objektivní důvod, proč by tyto bloky měly být spojité, Nebo pevné ve velikosti, nebo uspořádány určitým způsobem nyní. Předtím, než UNIX byl Multics, který netrpěl těmito omezeními. Zde je schematické znázornění jednoho z rozložení paměti té doby.

A typical 1980s style UNIX C program memory layout

35
jlettvin

stack, halda a data každého procesu ve virtuální paměti:

 stack, heap and static data

29
Yousha Aleayoub

Pár centů: myslím, že bude dobré kreslit grafickou a jednodušší paměť:

 This is my vision of process memory construction with simplification for more easy understanding wht happening


Šipky - ukazují, kde růstový zásobník a halda, velikost procesního zásobníku mají limit, definovaný v OS, omezení velikosti zásobníku podprocesů podle parametrů v rozhraní API pro vytváření vláken obvykle. Heap obvykle omezuje maximální velikost virtuální paměti, například 32 bitů 2-4 GB.

Tak jednoduchý způsob: proces haldy je obecné pro proces a všechny nitě uvnitř, pomocí pro přidělení paměti v běžném případě s něčím jako malloc ().

Stack je rychlá paměť pro ukládání do běžných ukazatelů a proměnných funkcí návratu funkcí, zpracovaných jako parametry ve funkčním volání, lokálních funkčních proměnných.

24
Maxim Akristiniy

Mám co sdílet, i když hlavní body jsou již pokryty.

Zásobník  

  • Velmi rychlý přístup.
  • Uloženo v paměti RAM.
  • Funkční volání jsou načtena spolu s místními proměnnými a funkčními parametry.
  • Prostor se automaticky uvolní, když program opustí rozsah.
  • Uloženo v sekvenční paměti.

Heap

  • Pomalý přístup srovnatelně s Stack.
  • Uloženo v paměti RAM.
  • Dynamicky vytvořené proměnné jsou zde uloženy, což později vyžaduje uvolnění přidělené paměti po použití.
  • Uloženo všude tam, kde se provádí alokace paměti.

Zajímavá poznámka:

  • Pokud by volání funkcí bylo uloženo v haldě, mělo by to za následek 2 nepořádné body:
    1. Díky sekvenčnímu ukládání v zásobníku je provádění rychlejší. Uložení v haldě by mělo za následek obrovskou spotřebu času, čímž by se celý program spustil pomaleji.
    2. Pokud by funkce byly uloženy v haldě (chaotický úložiště namířené ukazatelem), nebyl by žádný způsob, jak se vrátit zpět na adresu volajícího (který zásobník poskytuje v důsledku sekvenčního ukládání v paměti).
12
Pankaj Kumar Thapa

Spousta odpovědí je správná jako koncepty, ale musíme poznamenat, že hardware (tj. Mikroprocesor) potřebuje zásobník, aby povolil volání podprogramů (CALL v jazyce Assembly ..). (OOP kluci budou volat to metody)

V zásobníku, do kterého ukládáte zpáteční adresy a volání → Push/ret → pop je spravován přímo v hardwaru.

Zásobník můžete použít k předávání parametrů .. i když je pomalejší než používání registrů (řekl by mikroprocesorový guru nebo dobrá kniha BIOS z osmdesátých let ...)

  • Bez zásobníku no mikroprocesor může pracovat. (nemůžeme si představit program, ani v jazyce Assembly, bez podprogramů/funkcí)
  • Bez haldy to může. (Program jazyka sestavení může pracovat bez, protože halda je koncept OS, jako malloc, což je volání OS/Lib.

Využití zásobníku je rychlejší:

  • Je hardware a dokonce i Push/pop jsou velmi efektivní.
  • malloc vyžaduje vstup do režimu jádra, použijte zámek/semafor (nebo jiné synchronizační primitivy) provádějící nějaký kód a spravujte některé struktury potřebné pro sledování alokace.
8
ingconti

Wow! Tolik odpovědí a nemyslím si, že by jeden z nich měl pravdu ...

1) Kde a co jsou (fyzicky v paměti reálného počítače)?

Zásobník je paměť, která začíná jako nejvyšší adresa paměti přidělená obrazu programu a odtud odtud klesá. Je vyhrazena pro parametry volaných funkcí a pro všechny dočasné proměnné používané ve funkcích.

Existují dva hromady: veřejné a soukromé.

Soukromá halda začíná na hranici 16 bajtů (pro 64bitové programy) nebo 8bajtovou hranici (pro 32bitové programy) za posledním bajtem kódu v programu a odtud se zvyšuje hodnota. Je také nazývána výchozí haldy.

Pokud je soukromá halda příliš velká, překrývá oblast zásobníku, stejně jako zásobník překrývá haldu, pokud je příliš velká. Vzhledem k tomu, že zásobník začíná na vyšší adrese a pracuje směrem dolů na nižší adresu, můžete při správném hackování vytvořit zásobník tak velký, že bude překrýt oblast soukromé haldy a překrýt oblast kódu. Trik je pak dostatečně překrýt kódovou oblast, kterou můžete zavěsit do kódu. Je to trochu složitější a riskujete pád programu, ale je to snadné a velmi efektivní.

Veřejná halda je umístěna ve vlastním paměťovém prostoru mimo prostor programu. Je to právě tato paměť, která bude sifonována na pevný disk, pokud se paměťové zdroje dostanou do nedostatku.

2) Do jaké míry jsou řízeny operačním systémem nebo jazykovým runtime?

Zásobník je řízen programátorem, soukromá halda je spravována operačním systémem a veřejná halda není kontrolována nikým, protože se jedná o službu operačního systému - zadáváte požadavky a buď jsou udělovány nebo odepřeny.

2b) Jaký je jejich rozsah?

Všechny jsou pro program globální, ale jejich obsah může být soukromý, veřejný nebo globální.

2c) Co určuje velikost každého z nich?

Velikost zásobníku a soukromé haldy jsou určeny možnostmi runtime kompilátoru. Veřejná halda je inicializována za běhu pomocí parametru size.

2d) Co dělá jeden rychlejší?

Nejsou navrženy tak, aby byly rychlé, jsou navrženy tak, aby byly užitečné. Jak je programátor využívá, určuje, zda jsou "rychlé" nebo "pomalé".

REF:

https://norasandler.com/2019/02/18/Write-a-Compiler-10.html

https://docs.Microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap

https://docs.Microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate

3
ar18

alokační strategie pro ukládání dat pomocí programovacích jazyků 

PAMĚŤ V C - STACK, HEAP a STATIC

 enter image description here  MEMORY IN C – THE STACK, THE HEAP, AND STATIC

  1. OP: Do jaké míry jsou řízeny operačním systémem nebo jazykovým runtime?

Chci k této důležité otázce přidat několik věcí: 

OS a Common Language Runtime 

Klíčové komponenty.NET Framework  enter image description here Síťový rámec 4.5 Architektura  enter image description here

Součásti CLR  Components of CLR  enter image description here  enter image description here  enter image description here

0
leonidaa