it-swarm-eu.dev

Stack and Heap memory in Java

Jak jsem pochopil, v Javě obsahuje paměť zásobníku primitiva a vyvolávání metod a paměť haldy se používá k ukládání objektů.

Předpokládejme, že mám třídu

class A {
       int a ;
       String b;
       //getters and setters
}
  1. Kde budou uloženy primitivní a ve třídě A?

  2. Proč vůbec existuje paměť haldy? Proč nemůžeme všechno uložit do zásobníku?

  3. Když je objekt sbírán odpadky, je zásobník asociovaný s objektem zničen?

101

Základní rozdíl mezi stackem a haldy je životním cyklem hodnot.

Hodnoty zásobníku existují pouze v rámci funkce, ve které jsou vytvořeny. Jakmile se vrátí, jsou zahozeny.
Na haldě však existují hodnoty haldy. Jsou vytvářeny v určitém okamžiku a zničeny v jiném (buď pomocí GC nebo ručně, v závislosti na jazyce/runtime).

Now Java ukládá do zásobníku pouze primitiva. Tím se zásobník udržuje malý a pomáhá udržovat malé rámečky zásobníku malé, což umožňuje více vnořených hovorů.
Objekty jsou vytvářeny na haldě a v zásobníku jsou předávány pouze odkazy (které jsou zase primitivní).

Pokud tedy vytvoříte objekt, umístí se na hromadu se všemi proměnnými, které k němu patří, aby mohl přetrvávat i po návratu volání funkce.

106
back2dos

Kde jsou uložena primitivní pole?

Primitivní pole jsou uložena jako součást objektu, který je instancován někde . Nejjednodušší způsob, jak přemýšlet o tom, kde to je - je hromada. Nicméně , není tomu tak vždy. Jak je popsáno v Java teorie a praxe: Urban performance legends, recited :

JVM mohou používat techniku ​​zvanou úniková analýza, pomocí níž mohou zjistit, že určité objekty zůstávají po celou dobu své životnosti omezeny na jediné vlákno a že životnost je omezena životností daného rámce zásobníku. Takové objekty mohou být bezpečně přiděleny na hromadu namísto haldy. Ještě lepší je, že u malých objektů může JVM optimalizovat alokaci úplně a jednoduše zvednout pole objektu do registrů.

Tedy, kromě přísloví „objekt je vytvořen a pole je tam také“, nelze říci, zda je něco na haldě nebo na zásobníku. Všimněte si, že u malých objektů s krátkou životností je možné, že „objekt“ nebude existovat v paměti jako takové, a místo toho může mít svá pole umístěna přímo v registrech.

Článek končí:

JVM jsou překvapivě dobré na to, aby zjistili věci, o kterých jsme předpokládali, že je může vědět jen vývojář. Tím, že si JVM může vybrat mezi přidělením zásobníku a přidělením haldy případ od případu, můžeme získat výkonnostní výhody přidělení zásobníku bez toho, aby programátor zneklidnil, zda se má přidělit na zásobníku nebo na hromadu.

Pokud tedy máte kód, který vypadá takto:

void foo(int arg) {
    Bar qux = new Bar(arg);
    ...
}

tam, kde ... neumožňuje qux opustit tento obor, může být přiděleno qux místo toho na zásobníku. Toto je vlastně vítězství pro VM, protože to znamená, že nemusí být nikdy sbírány odpadky - zmizí, když opustí rozsah.

Více na úniková analýza na Wikipedii. Pro ty, kteří se chtějí ponořit do článků, Escape Analysis for Java od IBM. Pro ty, kteří přicházejí ze světa C #, možná najdete Zásobník je detail implementace a Pravda o typech hodnot od Eric Lipperta dobré čtení (jsou užitečné pro Java typy také proto, že mnoho konceptů a aspektů jsou stejné nebo podobné). Proč knihy .Net hovoří o přidělení paměti zásobníku a haldy? do toho také spadá.

Na whys stacku a haldy

Na hromadu

Proč tedy mít hromadu nebo hromadu? U věcí, které ponechávají prostor, může být zásobník drahý. Zvažte kód:

void foo(String arg) {
    bar(arg);
    ...
}

void bar(String arg) {
    qux(arg);
    ...
}

void qux(String arg) {
    ...
}

Parametry jsou také součástí zásobníku. V případě, že nemáte haldu, předáváte celou sadu hodnot v zásobníku. To je v pořádku pro "foo" A malé řetězce ... ale co by se stalo, kdyby někdo do tohoto řetězce vložil obrovský soubor XML. Každé volání zkopíruje celý obrovský řetězec do zásobníku - a , že by bylo docela zbytečné.

Místo toho je lepší umístit objekty, které mají nějaký život, mimo bezprostřední rozsah (předán do jiného oboru, zaseknutý ve struktuře, kterou udržuje někdo jiný atd.), Do jiné oblasti, která se nazývá halda.

Na zásobníku

Zásobník nepotřebujete . Dalo by se hypoteticky napsat jazyk, který nepoužívá hromadu (libovolné hloubky). Starý BASIC, o kterém jsem se dozvěděl v mládí, to udělal, člověk mohl provést pouze 8 úrovní volání gosub a všechny proměnné byly globální - neexistoval žádný zásobník.

Výhodou zásobníku je to, že když máte proměnnou, která existuje s oborem, při opuštění tohoto oboru se rámeček zásobníku objeví. Opravdu to zjednodušuje to, co je a co není. Program se přesune na jinou proceduru, nový rámeček zásobníku; program se vrátí k postupu a vy jste zpět v tom, který vidí váš aktuální rozsah; program opustí proceduru a všechny položky v zásobníku jsou uvolněny.

To opravdu usnadňuje život osobě, která píše runtime, aby kód mohl používat zásobník a haldu. Jednoduše mnoho konceptů a způsobů práce na kódu, které umožňují, aby osoba, která kód kóduje v jazyce, nemusela na ně výslovně myslet.

Povaha zásobníku také znamená, že se nemůže roztříštit. Fragmentace paměti je skutečný problém s haldy. Přiřaďte několik objektů, poté smetí nasbíráte prostřední a zkuste najít místo pro přidělení dalšího velkého. To je nepořádek. Být schopen místo toho dát do zásobníku místo toho znamená, že se tím nemusíte zabývat.

Když se něco sbírá

Když se něco sbírá, je to pryč. Sbírá se však pouze odpadky, protože na to již bylo zapomenuto - v programu již nejsou žádné odkazy na objekt, ke kterým lze přistupovat z aktuálního stavu programu.

Poukážu na to, že se jedná o velmi velké zjednodušení sběru odpadu. Existuje mnoho sběratelů odpadků (dokonce i v rámci Java) - můžete sběratele odpadků vylepšit pomocí různých příznaků ( docs ). Chová se odlišně a liší se nuance toho, jak každý dělá věci. trochu příliš hluboko na tuto odpověď. Možná si budete chtít přečíst Java Garbage Collection Basics , abyste získali lepší představu o tom, jak něco z toho funguje.

To znamená, že pokud je něco přiděleno na zásobníku, není to odpadky shromažďované jako součást System.gc() - je uvolněno, když se objeví rám zásobníku. Pokud je něco na haldě a odkazuje se na něco z hromady, nebude to v té době sebráno.

Proč na tom záleží?

Z velké části, jeho tradice. Učebnice napsané a třídy kompilátorů a různá dokumentace bitů dělají hodně o haldě a zásobníku.

Virtuální stroje dneška (JVM a podobné) však šly do značných délek, aby se pokusily udržet toto před programátorem skryté. Pokud vám nedojde jeden nebo druhý a potřebujete vědět proč (spíše než jen přiměřeně zvětšit úložný prostor), na tom nezáleží hodně.

Objekt je někde a je na místě, kde je přístupný správně a rychle po přiměřenou dobu, která existuje. Pokud je to na hromádce nebo na hromadě - na tom opravdu nezáleží.

49
user40980
  1. V haldě jako součást objektu, na který odkazuje ukazatel v zásobníku. tj. aab budou uloženy vedle sebe.
  2. Protože kdyby celá paměť byla paměť zásobníku, už by to nebylo efektivní. Je dobré mít malou, rychle přístupnou oblast, kde začínáme, a mít referenční položky v mnohem větší oblasti paměti, která zůstává. Toto je však nadbytečné, když je objekt jednoduše jednoduchým primitivem, který by zabíral přibližně stejné množství místa na zásobníku jako ukazatel na něj.
  3. Ano.
7
pdr
  1. Na haldě, pokud Java přiděluje instanci třídy na zásobníku jako optimalizaci poté, co se pomocí analýzy úniku prokáže, že to neovlivní sémantiku. Jedná se však o detail implementace, takže pro všechny praktické účely kromě mikro-optimalizace odpověď je "na hromadu".

  2. Paměť zásobníku musí být alokována a rozdána v posledním pořadí. Hromadná paměť může být přidělena a přidělena v libovolném pořadí.

  3. Když je objekt sbírán odpadky, neexistují žádné další odkazy, které na něj ukazují ze zásobníku. Pokud by existovaly, udržovaly by objekt naživu. Hromadné primitivy se nesbírají vůbec, protože se po návratu funkce automaticky zničí.

3
dsimcha

Zásobníková paměť se používá k ukládání lokálních proměnných a volání funkcí.

Zatímco haldy paměti se používá k ukládání objektů v Javě. Bez ohledu na to, kde je objekt vytvořen v kódu.

Kde bude primitivní a v class A být uložen?

V tomto případě je primitivní a spojeno s objektem třídy A. Vytváří se tak v paměti haldy.

Proč vůbec existuje paměť haldy? Proč nemůžeme všechno uložit do zásobníku?

  • Proměnné vytvořené v zásobníku budou mimo rozsah a automaticky budou zničeny.
  • Zásobník je mnohem rychlejší alokovat ve srovnání s proměnnými na haldě.
  • Proměnné na haldě musí zničit sběratel odpadu.
  • Pomalejší alokace ve srovnání s proměnnými na zásobníku.
  • Zásobník byste použili, pokud přesně víte, kolik dat je třeba přidělit před časem kompilace a není příliš velké. (Primitivní lokální proměnné se ukládají do zásobníku)
  • Haldu byste použili, pokud přesně nevíte, kolik dat budete potřebovat za běhu, nebo pokud potřebujete přidělit velké množství dat.

Když je objekt sbírán odpadky, je zásobník asociovaný s objektem zničen?

Sběratel odpadu pracuje v rámci paměti Heap, takže ničí objekty, které nemají referenční řetězec od kořene.

3
Premraj