Managementul memoriei în Java
⚡ Rezumat inteligent
Managementul memoriei în Java explică modul în care JVM împarte memoria de execuție între Stack, Heap, Codeși regiuni statice, modul în care se desfășoară referințele la obiecte în timpul apelurilor de metode și modul în care Garbage Collector recuperează obiectele inaccesibile, astfel încât aplicațiile să rămână stabile și fără pierderi de memorie.

Ce este memoria stivă în Java?
Memoria stivuită în Java este regiunea din memoria JVM care stochează cadrele de metodă, variabilele locale și variabilele de referință pentru fiecare fir de execuție. Stiva este întotdeauna accesată în ordinea „Ultimul intrat, primul ieșit”, astfel încât metoda invocată cel mai recent se află deasupra, iar variabilele sale locale sunt introduse și extrase odată cu cadrul.
Fiecare fir din Java Mașina virtuală primește propriul Stack, care menține invocările metodelor izolate și sigure pentru thread-uri prin design. Valorile locale primitive, cum ar fi int, boolean și double, se află direct în interiorul frame-ului, în timp ce referințele obiectelor stocate pe Stack indică obiecte alocate pe Heap.
Ce este memoria Heap în Java?
Memoria heap este regiunea JVM partajată care conține fiecare Java obiect și matrice create cu operatorul new, împreună cu orice variabile de referință care aparțin acelor obiecte ca și câmpuri de instanță. Spre deosebire de Stack, Heap-ul este partajat între toate firele de execuție, motiv pentru care accesul la obiecte necesită adesea sincronizare.
Heap-ul este zona gestionată de Garbage Collector. JVM-urile HotSpot moderne împart Heap-ul într-o Generație Tânără pentru obiectele cu durată scurtă de viață și o Generație Veche pentru obiectele cu durată lungă de viață, metadatele clasei fiind stocate într-o regiune nativă separată numită Metaspace.
Alocarea memoriei în Java
Alocarea memoriei în Java este procesul prin care JVM rezervă regiuni de memorie virtuală pentru variabile și instanțe de clase și structuri în timpul execuției programului. Memoria nu este alocată unui obiect la declarare; este creată doar o referință. Alocarea propriu-zisă a obiectului are loc prin operatorul new, astfel încât fiecare obiect se află în Heap.
Java Alocarea memoriei este împărțită în următoarele secțiuni:
- movilă
- Stivui
- Code
- Static
Această diviziune a memoriei este necesară pentru gestionarea eficientă a timpului de execuție.
- Code secțiunea conține compilarea dvs. cod octet.
- Stivui magazine de secție metode, variabile locale și variabile de referință.
- movilă secțiunea conține obiecte și poate conține, de asemenea, variabile de referință păstrate ca și câmpuri de instanță.
- Static secțiunea deține date statice și metode statice partajată în toate instanțele.
Diferența dintre variabila locală și variabila de instanță
Înțelegerea locului în care se află fiecare tip de variabilă în JVM ajută la explicarea comportamentului Stack și Heap. variabila de instanta este declarat în interiorul unei clase, dar în afara oricărei metodeși se află pe Heap ca parte a obiectului său.
class Student{ int num; // num is instance variable public void showData{}
A variabila locala este declarat în interiorul unei metode, inclusiv argumentele metodeiși se află pe Stiva din interiorul cadrului activ.
public void sum(int a){ int x = a + 3; // a, x are local variables }
Diferența dintre stack și heap
Stiva și Heap-ul rezolvă probleme diferite în interiorul JVM-ului. Stiva oferă fiecărui fir de execuție o alocare rapidă și deterministă pentru datele cu durată scurtă de viață legate de domeniul de aplicare al metodei, în timp ce Heap-ul oferă o regiune partajată pentru obiectele cu durată lungă de viață la care orice fir de execuție poate face referire. Scurtul videoclip de mai jos rezumă contrastul înainte de ghidul următor.
Clic aici dacă videoclipul nu este accesibil
Pentru a vedea cum cooperează Stack și Heap, luați în considerare o metodă main care apelează metoda m1.
public void m1{ int x = 20; }
În JVM Stack, se creează un frame pentru metoda m1.
Variabila x din m1 este creată și în cadrul pentru m1 pe Stivă, așa cum se arată în imaginea de mai jos.
Metoda m1 apelează apoi metoda m2. În Stivă, se creează un nou cadru pentru m2 deasupra cadrului pentru m1.
Variabilele locale b și c sunt, de asemenea, create în interiorul cadrului pentru m2 pe Stivă.
public void m2(int b){ boolean c; }
Apoi, m2 apelează metoda m3. Din nou, un cadru pentru m3 este creat în partea de sus a stivei, așa cum se arată mai jos.
Acum, să presupunem că metoda m3 creează un obiect pentru clasa Account, care are două variabile de instanta int p și int q.
class Account { int p; int q; }
Iată codul pentru metoda m3.
public void m3(){ Account ref = new Account(); // more code }
Instrucțiunea new Account() creează un obiect de tip Account în Heap.
Variabila de referință ref este creată pe Stiva în interiorul cadrului pentru m3.
Operatorul de atribuire face ca variabila de referință să indice către obiectul din Heap.
Odată ce metoda finalizează execuția, controlul revine la metoda apelantă, care în acest caz este metoda m2.
Cadrul pentru metoda m3 este golit din Stivă.
Deoarece variabila de referință nu mai indică obiectul din Heap, acel obiect devine eligibil pentru colectarea gunoiului.
Odată ce metoda m2 s-a terminat, aceasta este eliminată din Stivă și toate variabilele sale devin indisponibile. Același lucru se întâmplă și cu metoda m1, iar în cele din urmă controlul revine la metoda principală.
Ce se întâmplă dacă un obiect deține o altă referință ca variabilă de instanță?
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; }
În acest caz, variabila de referință copil se află pe Heap ca parte a obiectului A și, la rândul său, indică propriul obiect B, așa cum se arată mai jos.
În ce este colectarea gunoiului Java?
Colectarea gunoiului în Java este procesul prin care JVM efectuează automat gestionarea memoriei. Colectorul de gunoi găsește obiecte care nu mai sunt accesibile din nicio referință live și le recuperează memoria. Alocarea dinamică a memoriei se face prin intermediul operatorului new, iar memoria rămâne alocată până când programul nu mai deține nicio referință la obiect.
Când nu mai rămân referințe, obiectul este considerat a nu mai fi necesar, iar memoria pe care o ocupă poate fi recuperată. Nu există o nevoie explicită de a distruge un obiect deoarece Java gestionează automat dealocarea prin intermediul Colectorului de gunoi.
Tehnica din spatele acestui lucru este cunoscută sub numele de Colecția de gunoiProgramele care nu reușesc să elibereze memorie se blochează în cele din urmă atunci când nu mai rămâne nimic de alocat. Se spune că astfel de programe au scurgeri de memorie. Colectarea gunoiului în Java rulează automat pe toată durata de viață a programului, ceea ce elimină povara deallocării manuale și reduce riscul de scurgeri.
În C, prin contrast, programatorul este responsabil pentru eliberarea memoriei alocate dinamic prin funcția free(). Aici este locul unde Java gestionarea memoriei oferă un avantaj semnificativ.
JVM-urile HotSpot moderne includ mai multe colectoare de gunoi optimizate pentru diferite sarcini de lucru. Colectorul de gunoi G1 implicit vizează un debit echilibrat și timpi de pauză pe heap-uri de mai mulți gigabytes. ZGC și Shenandoah vizează pauze sub milisecunde pe heap-uri foarte mari, ceea ce le face...tractiv pentru servicii sensibile la latență. Alegerea colectorului potrivit și reglarea dimensiunilor Generației Tânăre, Generației Vechi și Metaspațiului reprezintă o abilitate esențială pentru Java muncă de performanță.
Notă: Toate obiectele sunt create în secțiunea Heap a memoriei, care este regiunea gestionată de Garbage Collector.
Exemplu: Pentru a învăța mecanismul de colectare a gunoiului în Java
Această demonstrație prezintă momentele în care referințele devin eligibile pentru Garbage Collection în cadrul unui program simplu.
Pas 1) Copiați următorul cod într-un editor.
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(); } }
Pas 2) Salvați, compilați și rulați codul. După cum se arată în diagramă, sunt create două obiecte și două variabile de referință.
Pas 3) Eliminați comentariile de la liniile 20, 21 și 22. Salvați, compilați și rulați codul.
Pas 4) După cum se arată în diagrama de mai jos, două variabile de referință indică acum același obiect.
Pas 5) Eliminați comentariile de la liniile 23 și 24. Salvați, compilați și rulați codul.
Pas 6) După cum se arată mai jos, s2 devine nul, dar s3 indică în continuare obiectul, deci obiectul nu este încă eligibil pentru Garbage Collection.
Pas 7) Eliminați comentariile de la liniile 25 și 26. Salvați, compilați și rulați codul.
Pas 8) În acest moment, nicio referință nu indică către obiect, deci acesta devine eligibil pentru Garbage Collector. Garbage Collector îl elimină din memorie și nu există nicio modalitate de a-l recupera.
Cum se șterge un obiect în Java?
Java nu oferă un operator de ștergere manuală, așadar abordarea standard este de a elimina fiecare referință la obiect, astfel încât Garbage Collector să îl poată recupera.
1) Pentru a face un obiect eligibil pentru Garbage Collection, atribuiți fiecărei variabile de referință care indică către acesta valoarea nulă.
2) Tipurile primitive nu sunt obiecte, deci nu li se poate atribui valoarea nulă. Spațiul lor de stocare este recuperat automat atunci când se extrage frame-ul Stack din jur.


















