Az automatikus memóriakezelés szemétgyűjtéssel az Android platform kulcsmechanizmusa, amely az ART virtuális gépre épül. A Google Android Documentation, 2026 szerint a szemétgyűjtő felszabadítja a fejlesztőt a kézi memóriakezelés alól, automatikusan eltávolítva azokat az objektumokat, amelyekre már nincs hivatkozás. GC nélkül minden objektumfoglalás explicit free vagy delete hívást igényelne, ami a Java ökoszisztémában másodpercenként millió objektummal fizikailag lehetetlen.
Főbb pontok
Garbage Collection (GC) — egy automatikus folyamat a program által már nem használt objektumok által elfoglalt memória észlelésére és felszabadítására. A mobilfejlesztés kontextusában a GC az Android platformon az ART virtuális gépen, valamint a szabványos Java Virtual Machine-en keresztül kerül alkalmazásra.
Ellentétben a kézi memóriakezeléssel rendelkező nyelvekkel (C, C++), ahol a programozónak explicit módon kell meghívnia a free vagy delete utasítást, a GC teljesen átveszi az objektumok életciklusának nyomon követését. A fejlesztő új objektumokat hoz létre a new operátor segítségével, a gyűjtő pedig meghatározza azt a pillanatot, amikor egy objektum elérhetetlenné válik — vagyis nincs többá aktív hivatkozás rá.
A fő mérőszám a GC hatékonyságára — a szünetidő (pause time) és az átbocsátóképesség (throughput). A szünet az az időszak, amikor az alkalmazás végrehajtása felfüggesztődik a gyűjtés elvégzéséhez. Mobilos környezetben a 8–16 milliszekundumnál hosszabb szünetek kihagyott képkockákként (jank) érzékelhetők.
A Google I/O 2019 adatai szerint az ART Android 10-ben 2–4 ms-ra csökkentette a tipikus GC szüneteket, ami 70%-kal kevesebb az Android 4.4-es Dalvikhoz képest. Ennek ellenére a memória helytelen kezelése — gyakori objektumfoglalás ciklusokban, szükségtelen ideiglenes példányok létrehozása — továbbra is a teljesítményproblémák fő oka marad.
Az összes GC megvalósítás Java-ban és Androidban néhány alapvető algoritmusra épül, amelyeket kombinálnak a szünetidő és a tisztítás teljessége közötti egyensúly elérése érdekében. Ezen algoritmusok megértése szükséges a GC-barát kód írásához.
Mark-and-Sweep — a legegyszerűbb algoritmus, amely két fázisban működik. A Mark fázisban a gyűjtő bejárja az objektumgráfot, a gyökérhivatkozásoktól (root set) — lokális változók, statikus mezők, szálveremek — kezdve. Minden elérhető objektum megjelölésre kerül egy live jelzővel. A Sweep fázisban a gyűjtő áthalad a teljes kupacon és felszabadítja a meg nem jelölt objektumok memóriáját.
Hátránya — a memóriafragmentáció: Sweep után a szabad területek váltakoznak a foglaltakkal, ami megnehezíti a nagy objektumok foglalását. Mobilos forgatókönyvekben ez kritikus, mert a kupac általában kicsi (64–512 MB Androidon).
Copying Collection két féltérre (semi-spaces) osztja a kupacot. Az aktív objektumok az egyik féltérből a másikba másolódnak tömören, megszakítás nélkül. A másolás után a régi féltér teljesen szabadnak nyilváníttatik. Az algoritmus teljesen megszünteti a fragmentációt, de kétszer annyi memóriát igényel.
Mobil környezetben a Copying Collection-t a generációs gyűjtők használják a fiatal objektumok gyors tisztítására, amelyek statisztikailag korán elhalnak (gyenge generáció hipotézis).
Generational Collection generációkra osztja a kupacot: Young Generation (fiatal objektumok) és Old Generation (öreg objektumok, amelyek több gyűjtést is túléltek). A fiatal generáció gyűjtése (Minor GC) gyakran és gyorsan történik, mivel a legtöbb objektum fiatalon elhal. Az öreg generáció gyűjtése (Major GC vagy Full GC) ritkábban fordul elő, de tovább tart.
// Generációs GC bemutató: a fiatal objektumok gyorsan elhalnak
void processItems(List<Item> items) {
List<Result> results = new ArrayList<>(); // éli az egész módszert
for (Item item : items) {
Result r = new Result(item.getValue()); // azonnal meghal
if (r.isValid()) {
process(r); // r szemétté válik
}
}
saveResults(results); // results átkerül az Old Gen-be
}
Ebben a példában a Result objektumok a cikluson belül jönnek létre és azonnal szemétté válnak — ideális jelöltek a Young GC számára. A results objektum tovább él és átkerül az Old Generation-be. A generációk szétválasztása lehetővé teszi a Minor GC számára, hogy a fiatal objektumokat ezredmásodpercek alatt tisztítsa, anélkül hogy hozzányülna a régi kupachoz.
Az Android a Dalvik VM-től az ART-ig (Android Runtime) vezető utat járta be, és a GC megvalósítása az egyik fő különbség köztük. A GC architektúra megértése Androidban segít olyan kód írásában, amely minimalizálja a szüneteket valódi eszközökön.
| Jellemző | Dalvik (4.4-ig) | ART (5.0+) |
|---|---|---|
| GC típus | Mark-and-Sweep Concurrent Mark-kal | Generációs + Concurrent |
| Tipikus szünet | 10–30 ms | 2–4 ms |
| Kompatkálás | Nem (csak fragmentáció nő) | Igen (háttérben, az alkalmazás megállítása nélkül) |
| AOT fordítás | JIT (Just-In-Time) | AOT + JIT (hibrid) |
Dalvik a Mark-and-Sweep és egy konkurens fázis kombinációját használta. A Concurrent Mark lehetővé tette az alkalmazás számára, hogy tovább dolgozzon az objektumgráf bejárása közben, de a Sweep fázis az összes szál leállítását igényelte (Stop-The-World). A kis RAM-mal rendelkező eszközökön (512 MB — 1 GB) a szünetek elérték a 30 ms-t, ami észrevehető lelassulást okozott a felületen. Ezenkívül a Dalvik nem kompaktálta a kupacot, így hosszú használat után a fragmentáció nőtt, és nagy objektumok (pl. Bitmap) foglalása OutOfMemoryError-t okozhatott, még elegendő összes szabad memória esetén is.
ART (Android Runtime) bevezetett egy generációs gyűjtőt konkurens kompaktálással. A kupac három régióra oszlik: Young, Mature (az Old Generation megfelelője) és Large Object Space (12 KB-nál nagyobb objektumok számára). A Young régió gyűjtése párhuzamosan történik a szálak leállítása nélkül a legtöbb esetben. Android 10+-ban megjelent a Concurrent Copying — a kompaktálás háttérszálban történik Stop-The-World nélkül.
Az ART architektúrájának köszönhetően a tipikus GC szünetek 2–4 ms-ra csökkentek, és a fiatal objektumok túlsúlyával jellemzett forgatókönyvekben 0.5–1 ms-ra. Ez lehetővé tette az Android eszközök számára, hogy stabil 60 FPS-t biztosítsanak, még aktív memóriahasználat mellett is.
A Java ökoszisztémában több GC megvalósítás létezik, mindegyik saját teljesítményprofillal. Az Android fejlesztéshez a választás az ART-ra korlátozódik, de a Java GC ismerete hasznos a mobilalkalmazások szerver részének írásakor és Kotlin Multiplatform fejlesztésnél.
Serial GC — egyszálú gyűjtő az alkalmazás teljes leállításával (Stop-The-World). Minden Mark, Sweep és Compact műveletet egy szál végez. A teljesítmény alacsony — nem használják mobil szerverekhez. Csak kis alkalmazásokhoz alkalmas, legfeljebb 100 MB kupaccal.
Parallel GC (még Throughput Collector ként ismert) több szálat használ a gyűjtés minden fázisához. A maximális átbocsátóképességre (throughput) összpontosít — minimalizálja a GC-re fordított időt az alkalmazás futási idejéhez képest. A -XX:+UseParallelGC jelzővel aktiválható a JVM-ben.
G1 (Garbage-First) GC — az alapértelmezett gyűjtő Java 9+-ban. A kupac 1–32 MB-os régiókra van osztva. A G1 előrejelzi a szünetidőt és igyekszik a beállított határértéken (alapértelmezett 200 ms) belül maradni. Prioritás: először a legnagyobb szemétmennyiséggel rendelkező régiók kerülnek tisztításra (innen a név). A G1 hatékony nagy kupacú szerverekhez (4–64 GB) és előrejelezhető szünetekhez.
// G1 GC bekapcsolása 100 ms cél szünetidővel
// java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar app.jar
public class MemoryMonitor {
private static final long THRESHOLD = 512 * 1024 * 1024; // 512 MB
public void checkHeapUsage() {
Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory();
if (used > THRESHOLD) {
System.out.println("A kupac használatának küszöbértéke túllépve: " + used);
System.out.println("Fontolja meg a foglalások csökkentését");
}
}
}
A kupac megfigyelése Runtime-on keresztül lehetővé teszi a memóriaszivárgások korai észlelését. Ha a used túllépi a maximális kupac 80%-át stabil működés mellett — ez egy lehetséges szivárgás vagy túlezértékű memóriahasználat jelzője az alkalmazás részéről.
Még a modern ART GC sem old meg minden problémát — a memória helytelen használata továbbra is a jank és ANR (Application Not Responding) fő oka. Nézzük át a fő forgatókönyveket és optimalizálási módszereket.
GC Pauses — az alkalmazás szálainak leállítása a gyűjtés során. A képernyőn ez kihagyott képkockákként jelenik meg, amikor a két képkocka közötti idő meghaladja a 16.6 ms-t (60 FPS). Ha a GC 30 ms-ig tart, csak egy képkocka rajzolódik ki kettő helyett — a felhasználó a felület akadását tapasztalja.
A hosszú szünetek fő okai: nagyszámú élő objektum az Old Generation-ben, kupac fragmentáció, gyakori Full GC. Diagnosztikához az Android Studio Profiler és a systrace használható.
A fő szabály a GC-barát kódhoz — minimalizálni a foglalt objektumok számát. Minden új objektum nemcsak memóriafoglalást igényel, hanem későbbi gyűjtést is. Még ha a GC gyors is, 1000 felesleges foglalás másodpercenként 1000 ellenőrzést jelent a gyűjtő számára.
Memóriaszivárgás akkor keletkezik, amikor egy objektum elérhető marad, bár már nincs rá szükség. A GC nem tudja eltávolítani az ilyen objektumot, és a memória fokozatosan kimerül. Tipikus okok: le nem iratkozott figyelők, statikus hivatkozások Activity-re, anonym osztályok, amelyek külső kontextust fognak le, és le nem zárt Cursor/InputStream.
// Memóriaszivárgás: anonim osztály hivatkozást tart az Activity-re
public void startTask() {
new Thread(new Runnable() { // implicit módon tartja a this-t (Activity)
@Override
public void run() {
// hosszú művelet...
System.out.println("Kész");
}
}).start();
}
// Javítás: statikus beágyazott osztály + WeakReference
private static class TaskRunnable implements Runnable {
private WeakReference<Activity> activityRef;
TaskRunnable(Activity activity) {
this.activityRef = new WeakReference<>(activity);
}
@Override
public void run() {
Activity act = activityRef.get();
if (act != null) {
// biztonságos munka az Activity-vel
}
}
}
Ebben a példában az anonim Runnable implicit hivatkozást tart az Activity-re. Amíg a szál él — az Activity nem gyűjthető be a GC által, még akkor sem, ha a felhasználó már bezárta a képernyőt. A WeakReference + static class javítás megszakítja ezt a láncot, és lehetővé teszi az Activity újrahasznosítását.
Gyakran ismételt kérdések
Az Android GC (ART) egy generációs gyűjtő konkurens kompaktálással, optimalizálva a korlátozott memóriájú mobileszközökhöz. A Java GC (G1, ZGC) szerver gyűjtők nagy kupacokkal és előrejelezhető szünetekkel. ART GC nem használ JVM jelzőket — minden beállítás automatikusan történik az operációs rendszer szintjén.
Stop-The-World — az a pillanat, amikor a gyűjtő leállítja az alkalmazás összes szálát, hogy biztonságosan bejárja az objektumgráfot vagy felszabadítsa a memóriát. Minél hosszabb az STW, annál észrevehetőbb a jank. Az ART a tipikus STW időt 2–4 ms-ra csökkentette a generációs architektúrának köszönhetően.
Használja az Android Studio Memory Profilert — megmutatja a kupac növekedését, a foglalások számát és lehetővé teszi a Heap Dump-ot. Mélyebb elemzéshez használja a LeakCanary-t — a könyvtár automatikusan észleli a szivárgásokat és megmutatja a GC gyűjtést akadályozó hivatkozási láncot.
Full GC — a kupac összes generációjának teljes gyűjtése, beleértve az Old Generation-t. Mobilalkalmazásokban a Full GC 50–200 ms-ig tarthat, észrevehető jank-ot vagy ANR-t okozva. Fő okok: kupac fragmentáció, memóriaszivárgások, az Old Generation küszöbértékének túllépése.
Kotlin strukturált konkurenciával rendelkező korutinokat kínál — a scope törlése automatikusan törli az összes gyermek korutint, megelőzve a szivárgásokat. Ezenkívül a Kotlinban van lazy delegált lusta inicializáláshoz és láthatósági tartomány operátorok, amelyek csökkentik az ideiglenes objektumok számát.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is