Tutorial Java – #8 Stiva metodelor si memoria Heap

Pentru a avea o intelegere profunda a progamarii orientate obiect in Java sau in orice alt limbaj orientat obiect (cum ar fi C #),trebuie sa stii cum sunt gestionate lucurile intern de catre procesul Java si de JVM (Java Virtual Machine). Desigur, sintaxa si  implementare principiilor POO (Programare Orientata Obiect) in Java sunt importante, dar vei avea o imagine mult mai clara cu privire la resursele necesare, memorie, performanta, transferul parametrilor, fire de executie si de eliberare a memoriei sau colectare a gunoiului (garbage collection), daca ai iti pui intrebari dincolo de Cum fac asta ? sau Cum scriu asta ?. Intrebari reale trebui sa fie Cum sau De ce se intampla asa ? (desigur, de la un punct, trebuie sa iei lucrurile asa cum sunt si sa mergi mai departe).
In acest tutorial voi descrie modul in care variabilele aplicatiei sunt gestionate, in ceea ce priveste locul unde sunt depozitate (Stack – Stiva sau Heap) si pentru cat timp.

Alte subiecte care fac parte din acest tutorial Java sunt accesibile prin intermediul Java Tutorial 6 – Cuprins

Ce este Stack-ul (Stiva) si Heap-ul

Pentru a mentine lucrurile la un nivel simple (daca ai cunostinte de programare in limbaj de asamblare, vei vedea ca prezentarea urmatoare reprezinta o abordare superficiala; daca vrei sa vezi cum sunt stau lucrurile in realitate poti citi Tutorial Limbaj de Asamblare (Assembler) Intel 8086 – Partea 5 – Proceduri) pornind de la ipoteza ca aplicatia prelucreaza date ce sunt stocate in anumite zone din RAM (Random Access Memory). Aceste zone se numesc:

Stack:

  • un spatiu de memorie rezervat pentru proces (aplicatie) de catre sistemul de operare;
    dimensiunea stivei este fixa si se stabileste, in faza de compilare, pe baza declaratiilor de variabile si alte optiuni de compilare;
  • este important sa se inteleaga faptul ca stiva este limitata, iar dimensiunea ei este fixa (un proces inceput, nu poates chimba dimensiunea stivei sale);
  • de cele mai multe ori, stiva este utilizat pentru a stoca variabilelor functiilor (argumente de intrare si variabile locale);
  • fiecare metoda are propia stiva (o zona in stiva procesului), inclusiv metoda speciala main, care este de asemenea o functie;
  • stiva unei metode exista doar pe durata de viata a acestei metode: din momentul apelarii pana in momentul terminarii functiei (return sau o exceptie); acest comportament se datoreaza chiar faptului ca stiva este limitata si spatiul este pus la dispozitia urmatoarei metode.

Heap:

  • un spatiu de memorie gestionate de sistemul de operare si utilizate de catre procese pentru a obtine spatiu suplimentar la executie (run-time);
  • acest spatiu exista la un nivel global, ceea ce inseamna ca orice proces poate folosi (desigur, procesele nu pot citi sau scrie intr-o zona din Heap rezervata altui proces);
  • rolul acestui memorii este de a oferi resurse suplimentare de memorie proceselor care au nevoie de spatiu suplimentar la run-time (de exemplu, va puteti gandi la o simpla aplicatie Java, care construieste un vector a carui dimensiune si elemente sunt primite de la consola);
  • spatiul necesar in Heap este determinat de functia new (este aceeasi functie folosita pentru a crea obiecte in Java).

Ce se memoreaza pe Stiva si ce in Heap

Bazandu-ne pe descrierile anterioare, hai sa analizam urmatorul program Java:

class Student{
    int age;            //variabila de instanta - atribut
    String name;     //variabila de instanta - atribut

    public Student()
    {
        this.age = 0;
        name = "Anonymous";
    }
    public Student(int Age, String Name)
    {
        this. age = Age;
        setName(Name);
    }
    public void setName(String Name)
    {
        this.name = Name;
    }
}

public class Main{
	public static void main(String[] args) {
            Student s;                   //variabila locala
            s = new Student(23,"Jonh");
            int noStudents = 1;          //variabila locala
	}
}

Pentru a determina spatiul minim (pentru ca ne concentram doar pe elemente importante si pastram lucrurile cat mai simplu posibil), vom analiza spatiul necesar pe Stiva a exemplului anterior. In paragraful urmator este descrisa separat, stiva fiecarei metoda.In realitate toate aceste stive sunt plasate in aceeasi zona fizica de memorie, stiva procesului sau a aplicatiei.

Incepem cu metoda main, deoarece procesul Java incepe si se termina cu aceasta. Principalele variabile locale, sau variabile stocate pe Stack ale acestei metode sunt:

  • referinta args(aceasta este un vector de String-uri);
  • referinta unui obiect de tip Student, numita s;
  • o valoare integer (4 octeti), noStudents;

Variabilele locale constructorului implicit sunt:

  • referinta obiectului creat, numita this;
  • asta e tot (obiectul si valorile sale sunt stocate in Heap).

Variabilele locale constructorului cu parametrii sunt:

  • referinta obiectului creat, numita this;
  • parametrul de intrare, Age, o valoare intreaga’;
  • parametrul de intrare, Name, o referinta de tip String.

Variabilele locale ale metodei setName sunt:

  • referinta obiectului care va apela aceasta metoda, numita this; (fiecare metoda nestatica din clasa este apelata de catre un obiect si referinta acelui obiect este transferata metodei prin this);
  • parametrul de intrare, Name, o referinta String.

Asa cum am precizat mai devreme, toate aceste stive asociate metodelor sunt, de fapt, parti ale unei singure stive asociata aplicatiei. Fiecare stiva exista pe durata executiei functiei. Daca analizam ordinea de apelare a acestor metode,putem defini o imagine de ansamblu, stiva de apeluri (call stack) a procesului.

Pentru exemplul anterior, atunci cand constructorul cu argumente este executat, stiva de apeluri arata astfel:

Call Stack Example

Call Stack Example

Dupa cum puteti vedea, in imaginea anterioara, stiva de apeluri, la un moment dat, este generata de toate metodele active (care nu si-au terminat executia). Deoarece metoda main este punct de intrare al procesului, este prima metoda din stiva de apeluri. Dupa acest moment, de fiecare data cand se apeleaza o metoda, acesta va fi plasata pe stiva de apeluri (call stack).

Pentru exemplul anterior, call stack-ul are dimensiunea maxima atunci cand se apeleaza metoda setName  din interiorul constructorului clasei Student.

Stack & Heap values for the Call Stack example

Stack & Heap values for the Call Stack example

In imaginea anterioara, sunt descrise variabilele locale metodelor,care sunt stocate pe stiva lor. De asemenea, puteti vedea ca valorile obiectelor (Student si String) sunt stocate in Heap.

Zonele de memorie din Heap sunt create de:

  • apel operatorului/metodei new si a constructorului clasei Student;
  • valoarea String este creat in timpul initializarii obiectului in interiorul constructorului clasei.

Care este durata de viata a variabilelor din Stack si Heap

Regula generala in ceea ce priveste durata de viata a variabilelor este data de faptul ca acestea exista, cel putin, pe durata procesarii lor prin diverse instructiuni.

Pentru variabilele de pe stiva, deoarece acestea sunt stocate pe stiva metodei in care ele sunt definite, ele exista atata timp cat metoda este executata. Deoarece metoda main este o metoda speciala (procesul incepe si se termina cu main) variabilele sale locale exista pe durata intregului proces.

Pentru alte metode, stiva exista doar din momentul apelullui pana la terminarea acestora (cu return sau din cauza unei exceptii).

Valorile din Heap (valori ale obiectelor sau alte referinte) exista atata timp cat avem o referinta care are ca valoare adresa acestei zone de memorie din Heap. in cazul in care zona de memorie din Heap nu mai poate fi accesat printr-o referinta (referinta nu mai exista sau are alta valoare/adresa), Garbage Collector-ul va elibera acel spatiu. (mai mult despre Garbage Collector in urmatorul tutorial).

Alte tipuri de variabile,cum ar fi atributelel statice definite intr-o clasa, sunt gestionate la fel, mentiunea ca ele sunt stocate pe stiva procesului, de obicei inainte de a aloca stive pentru metodele apelate.

Alte subiecte care fac parte din acest tutorial Java sunt accesibile prin intermediul Java Tutorial 6 – Cuprins