Bună!

Cred că nu veți fi prea surprins dacă vă spun că computerul dvs. are o cantitate limitată de memorie :) Chiar și un hard disk - în general de multe ori mai mare decât spațiul de stocare RAM - poate fi împachetat la capacitate maximă cu jocurile, emisiunile TV, și altele. Pentru a preveni acest lucru, trebuie să monitorizați starea curentă a memoriei și să ștergeți fișierele inutile de pe computer. Ce legătură are programarea Java cu toate acestea? Tot! La urma urmei, atunci când mașina Java creează orice obiect, alocă memorie pentru acel obiect.

Într-un program cu adevărat mare, sunt create zeci și sute de mii de obiecte, iar fiecare dintre ele are propria sa bucată de memorie alocată pentru el. Dar cât timp crezi că există toate aceste obiecte? „Trăiesc” tot timpul în care programul nostru rulează? Desigur că nu. Chiar și cu toate avantajele obiectelor Java, ele nu sunt nemuritoare :) Obiectele au propriul ciclu de viață. Astăzi vom face o mică pauză de la scrierea codului și ne vom uita la acest proces :) Mai mult, este foarte important pentru înțelegerea dvs. despre cum funcționează un program și cum sunt gestionate resursele. Deci, când începe viața unui obiect? Ca o persoană - de la naștere, adică de la creație.


Cat cat = new Cat(); // Here the lifecycle of our Cat object begins!

Mai întâi, Java Virtual Machine alocă cantitatea necesară de memorie pentru a crea obiectul. Apoi creează o referință la acea memorie. În cazul nostru, acea referință se numește cat, așa că o putem urmări. Apoi toate variabilele sale sunt inițializate, constructorul este numit și — ta-da! — obiectul nostru nou creat își trăiește propria viață :)

Durata de viață a obiectelor variază, așa că nu putem oferi numere exacte aici. În orice caz, trăiește ceva timp în interiorul programului și își îndeplinește funcțiile. Mai exact, un obiect este „viu” atâta timp cât există referiri la el. De îndată ce nu mai sunt referințe, obiectul „moare”. Exemplu:


public class Car {
  
   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");
       lamborghini = null;

   }

}

Obiectul mașină Lamborghini Diablo încetează să mai fie viu pe a doua linie a main()metodei. A existat o singură referință la ea, iar apoi acea referință a fost setată egală cu null. Deoarece nu au mai rămas referințe la Lamborghini Diablo, memoria alocată devine „gunoi”. O referință nu trebuie să fie setată la null pentru ca acest lucru să se întâmple:


public class Car {

   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");

       Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
       lamborghini = lamborghiniGallardo;
   }

}

Aici am creat un al doilea obiect și apoi am atribuit acest nou obiect referinței lamborghini. Acum Lamborghini Gallardoobiectul are două referințe, dar obiectul Lamborghini Diablonu are niciuna. Asta înseamnă că Diabloobiectul este acum gunoi. Acesta este momentul în care intră în joc mecanismul încorporat al Java numit garbage collector (GC).

Garbage Collector este un mecanism intern Java responsabil de eliberarea memoriei, adică de eliminarea obiectelor inutile din memorie. Există un motiv bun pentru care am ales aici o imagine a unui robot aspirator. La urma urmei, colectorul de gunoi funcționează aproape în același mod: în fundal, „călătorește” în jurul programului tău, adunând gunoiul practic fără efort din partea ta. Sarcina sa este de a elimina obiectele care nu mai sunt folosite în program.

Acest lucru eliberează memorie în computer pentru alte obiecte. Vă amintiți că la începutul lecției am spus că în viața obișnuită trebuie să monitorizați starea computerului și să ștergeți fișierele inutile? Ei bine, în cazul obiectelor Java, colectorul de gunoi face asta pentru tine. Colectorul de gunoi rulează în mod repetat pe măsură ce programul dumneavoastră rulează: nu trebuie să îl apelați în mod explicit sau să îi dați comenzi, deși acest lucru este posibil din punct de vedere tehnic. Mai târziu vom vorbi mai mult despre el și îi vom analiza mai detaliat activitatea.

Când colectorul de gunoi ajunge la un obiect, chiar înainte de a distruge obiectul, apelează la o metodă specială — finalize()— asupra obiectului. Această metodă poate elibera alte resurse utilizate de obiect. Metoda finalize()face parte din Objectclasa. equals()Aceasta înseamnă că , pe lângă metodele hashCode()și toString()pe care le-ați întâlnit anterior, fiecare obiect are această metodă. Diferă de alte metode prin faptul că este - cum ar trebui să spun asta - foarte capricios.

În special, nu este întotdeauna numit înainte de distrugerea unui obiect. Programarea este un efort precis. Programatorul îi spune computerului să facă ceva, iar computerul o face. Presupun că ești deja obișnuit cu acest comportament, așa că la început ți-ar putea fi dificil să accepți următoarea idee: „Înainte de distrugerea obiectelor, metoda finalize()clasei Objecteste numită. Sau poate că nu se numește. Totul depinde de norocul tău!”

Totuși, este adevărat. Mașina Java însăși determină dacă să apeleze sau nu finalize()metoda, de la caz la caz. De exemplu, să încercăm să rulăm următorul cod ca experiment:


public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public Cat() {
   }

   public static void main(String[] args) throws Throwable {
       for (int i = 0 ; i < 1000000; i++) {
           Cat cat = new Cat();
           cat = null; // This is when the first object becomes available to the garbage collector
       }
   }

   @Override
   protected void finalize() throws Throwable {
       System.out.println("Cat object destroyed!");
   }
}

Creăm un Catobiect și apoi în următoarea linie de cod setăm singura sa referință egală cu nul. Și facem asta de un milion de ori. Am suprasolicitat în mod explicit finalize()metoda, astfel încât să imprime un șir pe consolă de un milion de ori (o dată pentru fiecare dată când distruge un Catobiect). Dar nu! Mai exact, a rulat doar de 37.346 de ori pe computerul meu! Adică, doar o dată din 27 de ori mașina Java instalată pe mașina mea a decis să apeleze finalize()metoda.

În alte cazuri, colectarea gunoiului s-a făcut fără ea. Încercați să rulați acest cod pentru dvs.: cel mai probabil, veți obține un rezultat diferit. După cum puteți vedea, finalize()cu greu poate fi numit un partener de încredere :) Deci, un mic sfat pentru viitor: nu vă bazați pe finalize()metoda de a elibera resursele critice. Poate că JVM-ul îl va apela sau poate nu. Cine ştie?

Dacă, în timp ce obiectul tău este în viață, deține niște resurse care sunt super importante pentru performanță, de exemplu, o conexiune la o bază de date deschisă, este mai bine să creezi o metodă specială în clasa ta pentru a le elibera și apoi să o apelezi explicit când obiectul nu mai este. Necesar. Astfel vei ști cu siguranță că performanța programului tău nu va avea de suferit. De la bun început, am spus că lucrul cu memorie și eliminarea gunoiului este foarte important, iar acest lucru este adevărat. Manipularea incorectă a resurselor și înțelegerea greșită a modului în care obiectele inutile sunt curățate poate duce la scurgeri de memorie. Aceasta este una dintre cele mai cunoscute greșeli de programare.

Dacă programatorii își scriu codul incorect, poate fi alocată memorie nouă pentru obiectele nou create de fiecare dată, în timp ce obiectele vechi, inutile, s-ar putea să nu fie disponibile pentru îndepărtare de către colectorul de gunoi. Deoarece am făcut o analogie cu un robot aspirator, imaginați-vă ce s-ar întâmpla dacă, înainte de a porni robotul, împrăștiați șosete prin casă, spargeți o vază de sticlă și lăsați blocuri Lego peste tot pe podea. Robotul va încerca să-și facă treaba, desigur, dar la un moment dat se va bloca.

Pentru a permite aspiratorului robot să funcționeze corect, trebuie să mențineți podeaua în stare bună și să îndepărtați orice lucru pe care robotul nu se poate descurca. Același principiu se aplică și colectorului de gunoi din Java. Dacă într-un program au rămas multe obiecte care nu pot fi curățate (cum ar fi un șosetă sau un bloc de construcție Lego pentru aspiratorul nostru robot), la un moment dat vei rămâne fără memorie. Și s-ar putea să nu fie doar programul dvs. cel care va îngheța - orice alt program care rulează pe computer poate fi afectat. De asemenea, s-ar putea să nu aibă suficientă memorie.

Nu trebuie să memorezi asta. Trebuie doar să înțelegeți principiul din spatele modului în care funcționează.