Управление на паметта в Java
⚡ Умно обобщение
Управление на паметта в Java обяснява как JVM разделя паметта по време на изпълнение между Stack, Heap, Codeи статични региони, как се осъществява потокът от референции към обекти по време на извиквания на методи и как Garbage Collector възстановява недостъпни обекти, така че приложенията да останат стабилни и без изтичане на памет.

Какво представлява стековата памет Java?
Паметта в стека Java е областта от JVM паметта, която съхранява рамки на методи, локални променливи и референтни променливи за всяка нишка. Достъпът до стека винаги се осъществява в реда „последен влязъл, първи излязъл“, така че най-скоро извиканият метод е отгоре, а неговите локални променливи се добавят и извличат заедно с рамката.
Всяка нишка в Java Виртуалната машина получава собствен стек (stack), който държи извикванията на методи изолирани и нишкобезопасни по дизайн. Примитивни локални стойности като int, boolean и double се намират директно във фрейма (frame), докато обектните препратки, съхранени в стека (stack), сочат към обекти, разпределени в heap-а (heap).
Какво представлява купната памет (Heap Memory)? Java?
Купната памет е споделеният JVM регион, който съдържа всички Java обект и масив, създадени с оператора new, заедно с всички референтни променливи, които принадлежат на тези обекти като полета на екземпляра. За разлика от Stack, Heap се споделя между всички нишки, поради което достъпът до обекти често изисква синхронизация.
Купът (Heap) е областта, управлявана от Garbage Collector. Съвременните HotSpot JVM разделят купа на младо поколение за краткотрайни обекти и старо поколение за дълготрайни обекти, като метаданните на класа се съхраняват в отделен нативен регион, наречен Metaspace.
Разпределение на паметта в Java
Разпределение на паметта в Java е процесът, чрез който JVM заделя виртуални региони на паметта за променливи и екземпляри на класове и структури по време на изпълнение на програмата. Паметта не се разпределя към обект при декларацията; създава се само препратка. Действителното разпределение на обекти се извършва чрез оператора new, така че всеки обект се намира в Heap паметта.
- Java Разпределението на паметта е разделено на следните секции:
- купчина
- Стек
- Code
- Статичен
Това разделение на паметта е необходимо за ефективно управление на средата за изпълнение.
- - Code разделът съдържа компилираните от вас байткод.
- - Стек секционни магазини методи, локални променливи и референтни променливи.
- - купчина раздел съдържа обекти и може също да съдържа референтни променливи, съхранявани като полета на екземпляри.
- - Статичен секцията държи статични данни и статични методи споделено във всички инстанции.
Разлика между локална и инстанционна променлива
Разбирането къде се намира всеки вид променлива в JVM помага да се обясни поведението на Stack и Heap. променлива на екземпляра се декларира вътре в клас, но извън който и да е методи се намира в купчината памет (Heap) като част от обекта си.
class Student{ int num; // num is instance variable public void showData{}
A локална променлива се декларира вътре в метод, включително аргументи на методаи се намира на стека вътре в активния кадър.
public void sum(int a){ int x = a + 3; // a, x are local variables }
Разлика между Stack и Heap
Стекът (Stack) и купчината (Heap) решават различни проблеми в JVM. Стекът (Stack) предоставя на всяка нишка бързо, детерминистично разпределение за краткотрайни данни, обвързани с обхвата на метода, докато купчината (Heap) предоставя споделена област за дълготрайни обекти, към които всяка нишка може да се обърне. Краткото видео по-долу обобщава контраста преди следващото ръководство.
Кликнете тук ако видеото не е достъпно
За да видите как Stack и Heap си сътрудничат, разгледайте основен метод, който извиква метод m1.
public void m1{ int x = 20; }
В JVM стека се създава рамка за метод m1.
Променливата x в m1 също се създава във фрейма за m1 на стека, както е показано на изображението по-долу.
След това метод m1 извиква метод m2. В стека (Stak) се създава нов кадър за m2, върху кадъра за m1.
Локалните променливи b и c също се създават вътре в рамката за m2 на стека.
public void m2(int b){ boolean c; }
След това, m2 извиква метода m3. Отново, рамка за m3 се създава на върха на стека, както е показано по-долу.
Да кажем, че метод m3 създава обект за клас Account, който има два инстанционни променливи int p и int q.
class Account { int p; int q; }
Ето кода за метод m3.
public void m3(){ Account ref = new Account(); // more code }
Изразът new Account() създава обект Account в купчината памет (Heap).
Референтната променлива ref се създава на стека вътре в рамката за m3.
Операторът за присвояване кара референтната променлива да сочи към обекта в Heap.
След като изпълнението на метода приключи, контролът се връща към извикващия метод, който в този случай е метод m2.
Рамката за метод m3 се изчиства от стека.
Тъй като референтната променлива вече не сочи към обекта в Heap, този обект става допустим за събиране на боклука.
След като метод m2 приключи, той се премахва от стека и всички негови променливи стават недостъпни. Същото се случва и с метод m1 и в крайна сметка управлението се връща към главния метод.
Ами ако даден обект съдържа друга препратка като своя променлива на екземпляр?
public static void main(String args[]) { A parent = new A(); // more code } class A { B child = new B(); int e; } class B { int c; int d; }
В този случай, референтната променлива child се намира в Heap като част от обекта A и от своя страна сочи към собствения си обект B, както е показано по-долу.
Какво представлява събирането на боклук Java?
Извозване на боклук в Java е процесът, чрез който JVM извършва автоматично управление на паметта. Garbage Collector намира обекти, които вече не са достъпни от никоя активна препратка и освобождава паметта им. Динамичното разпределение на паметта се осъществява чрез оператора new и паметта остава разпределена, докато програмата вече не съдържа препратка към обекта.
Когато не останат препратки, обектът се счита за ненужен и паметта, която заема, може да бъде освободена. Няма изрична нужда от унищожаване на обект, защото Java обработва освобождаването на място автоматично чрез Garbage Collector.
Техниката зад това е известна като Събиране на боклукПрограмите, които не успеят да освободят памет, в крайна сметка се сриват, когато не остане нищо за разпределяне. За такива програми се казва, че имат изтичане на памет. Извозване на боклук в Java работи автоматично през целия жизнен цикъл на програмата, което премахва тежестта на ръчното освобождаване от разпределение и намалява риска от течове.
В C, за разлика от това, програмистът е отговорен за освобождаването на памет, разпределена динамично чрез функцията free(). Това е мястото, където Java Управлението на паметта предлага силно предимство.
Съвременните HotSpot JVM-и предлагат няколко Garbage Collector-а, настроени за различни натоварвания. Стандартният G1 Garbage Collector е насочен към балансирана пропускателна способност и време за пауза върху многогигабайтови купчини. ZGC и Shenandoah се стремят към паузи от под милисекунда върху много големи купчини, което ги прави...tracтивно за услуги, чувствителни към латентност. Изборът на правилния колектор и настройването на размерите на младото поколение, старото поколение и метапространството е основно умение за Java изпълнение.
Забележка: Всички обекти се създават в секцията Heap на паметта, която е областта, управлявана от Garbage Collector.
Пример: За да научите механизма за събиране на боклук в Java
Това ръководство показва кога препратките стават допустими за събиране на боклук в рамките на проста програма.
Стъпка 1) Копирайте следния код в редактор.
class Student{ int a; int b; public void setData(int c,int d){ a=c; b=d; } public void showData(){ System.out.println("Value of a = "+a); System.out.println("Value of b = "+b); } public static void main(String args[]){ Student s1 = new Student(); Student s2 = new Student(); s1.setData(1,2); s2.setData(3,4); s1.showData(); s2.showData(); //Student s3; //s3=s2; //s3.showData(); //s2=null; //s3.showData(); //s3=null; //s3.showData(); } }
Стъпка 2) Запазете, компилирайте и изпълнете кода. Както е показано на диаграмата, създават се два обекта и две референтни променливи.
Стъпка 3) Разкоментирайте редове 20, 21 и 22. Запазете, компилирайте и изпълнете кода.
Стъпка 4) Както е показано на диаграмата по-долу, две референтни променливи сега сочат към един и същ обект.
Стъпка 5) Разкоментирайте редове 23 и 24. Запазете, компилирайте и изпълнете кода.
Стъпка 6) Както е показано по-долу, s2 става null, но s3 все още сочи към обекта, така че обектът все още не е допустим за събиране на боклука.
Стъпка 7) Разкоментирайте редове 25 и 26. Запазете, компилирайте и изпълнете кода.
Стъпка 8) В този момент никакви препратки не сочат към обекта, така че той става допустим за събиране на боклука. Събирачът на боклука го премахва от паметта и няма начин да бъде извлечен.
Как да изтриете обект в Java?
Java не предоставя оператор за ръчно изтриване, така че стандартният подход е да се премахне всяка препратка към обекта, за да може Garbage Collector да го възстанови.
1) За да направите обект допустим за събиране на боклук, присвойте на всяка референтна променлива, която сочи към него, стойност null.
2) Примитивните типове не са обекти, така че не може да им се присвои null. Тяхната памет се освобождава автоматично, когато околната рамка Stack бъде извадена.


















