Android Runtime (ART) și mașina virtuală Dalvik utilizează paginarea și maparea memoriei (mmapping) pentru a gestiona memoria. Acest lucru înseamnă că orice memorie pe care o modifică o aplicație – fie prin alocarea de obiecte noi, fie prin atingerea paginilor mmapate – rămâne rezidentă în RAM și nu poate fi paginată. Singura modalitate de a elibera memoria de la o aplicație este de a elibera referințele obiectelor pe care aplicația le deține, punând memoria la dispoziția colectorului de gunoi. Aceasta cu o singură excepție: orice fișier mmappat fără modificări, cum ar fi codul, poate fi paginat din RAM dacă sistemul dorește să utilizeze acea memorie în altă parte.
Această pagină explică modul în care Android gestionează procesele aplicațiilor și alocarea memoriei. Pentru mai multe informații despre cum să gestionați memoria mai eficient în aplicația dumneavoastră, consultați Gestionarea memoriei aplicației dumneavoastră.
Colectarea gunoiului
Un mediu de memorie gestionată, cum ar fi mașina virtuală ART sau Dalvik, ține evidența fiecărei alocări de memorie. Odată ce stabilește că o bucată de memorie nu mai este utilizată de program, o eliberează înapoi în heap, fără nicio intervenție din partea programatorului. Mecanismul de recuperare a memoriei nefolosite în cadrul unui mediu de memorie gestionată este cunoscut sub numele de garbage collection. Colectarea gunoiului are două obiective: găsirea obiectelor de date dintr-un program care nu pot fi accesate în viitor; și recuperarea resurselor utilizate de acele obiecte.
Grămada de memorie a Android este una generațională, ceea ce înseamnă că există diferite găleți de alocări pe care le urmărește, pe baza duratei de viață și a dimensiunii preconizate a unui obiect alocat. De exemplu, obiectele alocate recent aparțin generației Young. Atunci când un obiect rămâne activ suficient de mult timp, acesta poate fi promovat la o generație mai veche, urmată de o generație permanentă.
Fiecare generație de heap are propria limită superioară dedicată privind cantitatea de memorie pe care o pot ocupa obiectele de acolo. De fiecare dată când o generație începe să se umple, sistemul execută un eveniment de colectare a gunoiului în încercarea de a elibera memorie. Durata colectării gunoiului depinde de generația de obiecte pe care o colectează și de câte obiecte active se află în fiecare generație.
Chiar dacă colectarea gunoiului poate fi destul de rapidă, aceasta poate afecta performanța aplicației dumneavoastră. În general, nu controlați din interiorul codului dvs. când are loc un eveniment de garbage collection. Sistemul are un set de criterii de funcționare pentru a determina momentul în care se efectuează colectarea gunoiului. Atunci când criteriile sunt îndeplinite, sistemul oprește execuția procesului și începe colectarea gunoiului. În cazul în care colectarea gunoiului are loc în mijlocul unei bucle de procesare intensivă, cum ar fi o animație sau în timpul redării muzicii, aceasta poate crește timpul de procesare. Această creștere poate, potențial, să împingă execuția codului în aplicația dvs. dincolo de pragul recomandat de 16 ms pentru o redare eficientă și fluidă a cadrelor.
În plus, fluxul dvs. de cod poate efectua tipuri de lucru care forțează evenimentele de colectare a gunoiului să apară mai des sau le face să dureze mai mult decât în mod normal. De exemplu, dacă alocați mai multe obiecte în partea cea mai interioară a unei bucle for-loop în timpul fiecărui cadru al unei animații de amestecare alfa, este posibil să vă poluați heap-ul de memorie cu o mulțime de obiecte. În această circumstanță, colectorul de gunoi execută mai multe evenimente de colectare a gunoiului și poate degrada performanța aplicației dumneavoastră.
Pentru mai multe informații generale despre colectarea gunoiului, consultați Colectarea gunoiului.
Partajarea memoriei
Pentru a încadra tot ce are nevoie în memoria RAM, Android încearcă să împartă paginile de RAM între procese. Poate face acest lucru în următoarele moduri:
- Fiecare proces de aplicație este bifurcat dintr-un proces existent numit Zygote. Procesul Zygote pornește atunci când sistemul pornește și încarcă codul și resursele comune ale cadrului (cum ar fi temele de activitate). Pentru a porni un nou proces de aplicație, sistemul face o bifurcație a procesului Zygote, apoi încarcă și rulează codul aplicației în noul proces. Această abordare permite ca cea mai mare parte a paginilor de memorie RAM alocate pentru codul și resursele cadrului să fie partajate între toate procesele aplicației.
- Majoritatea datelor statice sunt mmapate într-un proces. Această tehnică permite ca datele să fie partajate între procese și, de asemenea, permite ca acestea să fie paginate atunci când este necesar. Exemple de date statice includ: Codul Dalvik (prin plasarea acestuia într-un fișier
.odex
pre-legat pentru mmapping direct), resursele aplicației (prin proiectarea tabelului de resurse pentru a fi o structură care poate fi mmapată și prin alinierea intrărilor zip din APK) și elementele tradiționale ale proiectului, cum ar fi codul nativ în fișiere.so
. - În multe locuri, Android împarte aceeași memorie RAM dinamică între procese folosind regiuni de memorie partajată alocate explicit (fie cu ashmem, fie cu gralloc). De exemplu, suprafețele ferestrelor utilizează memoria partajată între aplicație și compozitorul de ecran, iar tampoanele cursorului utilizează memoria partajată între furnizorul de conținut și client.
Datorită utilizării extinse a memoriei partajate, determinarea cantității de memorie pe care o utilizează aplicația dvs. necesită atenție. Tehnicile pentru a determina în mod corespunzător utilizarea memoriei de către aplicația dumneavoastră sunt discutate în Investigarea utilizării RAM.
Alocarea și recuperarea memoriei aplicației
Heap-ul Dalvik este constrâns la un singur interval de memorie virtuală pentru fiecare proces de aplicație. Aceasta definește dimensiunea logică a heap-ului, care poate crește în funcție de necesități, dar numai până la o limită pe care sistemul o definește pentru fiecare aplicație.
Dimensiunea logică a heap-ului nu este aceeași cu cantitatea de memorie fizică utilizată de heap. Atunci când inspectează heap-ul aplicației dvs., Android calculează o valoare numită Dimensiunea proporțională a setului (PSS), care ține cont atât de paginile murdare, cât și de cele curate care sunt partajate cu alte procese – dar numai într-o cantitate proporțională cu numărul de aplicații care împart acea memorie RAM. Acest total (PSS) este ceea ce sistemul consideră a fi amprenta fizică a memoriei dumneavoastră. Pentru mai multe informații despre PSS, consultați ghidul Investigarea utilizării RAM.
Heap-ul Dalvik nu compactează dimensiunea logică a heap-ului, ceea ce înseamnă că Android nu defragmentează heap-ul pentru a închide spațiul. Android poate micșora dimensiunea logică a heap-ului doar atunci când există spațiu nefolosit la sfârșitul heap-ului. Cu toate acestea, sistemul poate totuși să reducă memoria fizică utilizată de heap. După colectarea gunoiului, Dalvik parcurge heap-ul și găsește paginile nefolosite, apoi returnează aceste pagini către kernel folosind madvise. Astfel, alocările și dezalocările perechi de bucăți mari ar trebui să ducă la recuperarea întregii (sau aproape întregii) memorii fizice utilizate. Cu toate acestea, recuperarea memoriei din alocări mici poate fi mult mai puțin eficientă, deoarece pagina utilizată pentru o alocare mică poate fi încă partajată cu altceva care nu a fost încă eliberată.
Restrângerea memoriei aplicațiilor
Pentru a menține un mediu multi-tasking funcțional, Android stabilește o limită dură a dimensiunii heap-ului pentru fiecare aplicație. Limita exactă a dimensiunii heap variază de la un dispozitiv la altul, în funcție de cantitatea de memorie RAM pe care dispozitivul o are disponibilă în general. Dacă aplicația dvs. a atins capacitatea heap și încearcă să aloce mai multă memorie, aceasta poate primi un mesaj OutOfMemoryError
.
În unele cazuri, este posibil să doriți să interogați sistemul pentru a determina exact cât spațiu heap aveți disponibil pe dispozitivul curent – de exemplu, pentru a determina cât de multe date este sigur să păstrați într-o memorie cache. Puteți interoga sistemul pentru această cifră prin apelarea getMemoryClass()
. Această metodă returnează un număr întreg care indică numărul de megaocteți disponibili pentru heap-ul aplicației dumneavoastră.
Schimbarea aplicațiilor
Atunci când utilizatorii trec de la o aplicație la alta, Android păstrează aplicațiile care nu sunt în prim-plan – adică nu sunt vizibile pentru utilizator sau nu rulează un serviciu în prim-plan, cum ar fi redarea de muzică – într-o memorie cache. De exemplu, atunci când un utilizator lansează pentru prima dată o aplicație, se creează un proces pentru aceasta; dar când utilizatorul părăsește aplicația, procesul respectiv nu se închide. Sistemul păstrează procesul în memoria cache. Dacă utilizatorul revine mai târziu la aplicație, sistemul reutilizează procesul, făcând astfel ca comutarea aplicației să fie mai rapidă.
Dacă aplicația dvs. are un proces în cache și reține resurse de care nu are nevoie în prezent, atunci aplicația dvs. – chiar și atunci când utilizatorul nu o folosește – afectează performanța generală a sistemului. Pe măsură ce sistemul rămâne fără resurse, cum ar fi memoria, acesta ucide procesele din memoria cache. De asemenea, sistemul ține cont de procesele care rețin cea mai multă memorie și le poate încheia pentru a elibera memorie RAM.
Notă: Cu cât aplicația dumneavoastră consumă mai puțină memorie în timp ce se află în memoria cache, cu atât mai mari sunt șansele ca aceasta să nu fie ucisă și să poată fi reluată rapid. Cu toate acestea, în funcție de cerințele instantanee ale sistemului, este posibil ca procesele din memoria cache să fie terminate în orice moment, indiferent de utilizarea resurselor.
Pentru mai multe informații despre modul în care procesele sunt stocate în memoria cache în timp ce nu rulează în prim-plan și despre modul în care Android decide care dintre ele pot fi ucise, consultați ghidul Procese și fire de execuție.