Аутоматско управљање меморијом кроз сакупљање отпада је кључни механизам платформе Android, заснован на виртуелној машини ART. Према Google Android Documentation, 2026, сакупљач отпада ослобађа програмера речног управљања меморијом, аутоматски уклањајући објекте који више немају референце. Без GC, свако додељивање објекта би захтевало јасно позивање free или delete, што је у Java екосистему са милионима објеката у секунди физички немогуће.
Главно
Garbage Collection (GC) — је аутоматски процес откривања и ослобађања меморије коју заузимају објекти који више нису у употреби у програму. У контексту мобилног развоја, GC се примењује на платформи Android кроз виртуелну машину ART, као и у стандардној Java Virtual Machine.
За разлику од језика са речним управљањем меморијом (C, C++), где програмер мора јасно да позове free или delete, GC у потпуности преузима задатак праћења животног циклуса објеката. Програмер креира нове објекте кроз оператор new, а сакупљач одређује тренутак када објекат постаје недоступан — односно ниједна активна референца више не указује на њега.
Основна метрика ефикасности GC — време паузе (pause time) и проток (throughput). Пауза је интервал у коме се обуставља извршавање апликације због сакупљања. У мобилном окружењу, паузе дуже од 8–16 милисекунди су приметне као пропуштени оквири (jank).
Према подацима Google I/O 2019, ART у Android 10 је смањио типичне GC паузе на 2–4 ms, што је 70% мање у поређењу са Dalvik-ом у Android 4.4. Ипак, неправилан рад са меморијом — честа алокација објеката у петљама, креирање привремених инстанци без потребе — остаје главни узрок проблема са перформансама.
Све GC имплементације у Java и Android-у заснивају се на неколико фундаменталних алгоритама који се комбинују да би се постигао баланс између времена паузе и потпуности чишћења. Разумевање ових алгоритама је потребно за писање GC-friendly кода.
Mark-and-Sweep — најједноставнији алгоритам који ради у две фазе. У фази Mark, сакупљач обилази граф објеката, почевши од коренских референци (root set) — локалних промењивих, статичких поља, стекова нити. Сваки доступан објекат се означава ознаком live. У фази Sweep, сакупљач пролази кроз цеокупно гомиле и ослабађа меморију неозначених објеката.
Недостатак је фрагментација меморије: након Sweep-а, слободни делови се изменују са заузетима, што отежава додељивање великих објеката. У мобилним сценаријима ово је критично јер је гомиле обично мало (64–512 MB на Android-у).
Copying Collection дели гомиле на два полупростора (semi-spaces). Активни објекти се копирају из једног полупростора у други компактно, без прекида. Након копирања, стари полупростор се проглашава потпуно слободним. Алгоритам потпуно елиминише фрагментацију, али захтева два пута више меморије.
У мобилним окружењима, Copying Collection користе генерацијски сакупљачи за брзо чишћење младих објеката, који статистички рано умиру (u0438потеза слабе генерације).
Generational Collection дели гомиле на генерације: Young Generation (млади објекти) и Old Generation (стари који су преживели неколико сакупљања). Сакупљање младе генерације (Minor GC) се обавља често и брзо, јер већина објеката умире млада. Сакупљање старе генерације (Major GC или Full GC) дешава се ређе, али траје дуже.
// Демонстрација генерацијског GC: млади објекти умиру брзо
void processItems(List<Item> items) {
List<Result> results = new ArrayList<>(); // живи целом методом
for (Item item : items) {
Result r = new Result(item.getValue()); // умире тренутно
if (r.isValid()) {
process(r); // r постаје отпад
}
}
saveResults(results); // results прелази у Old Gen
}
У овом примеру, објекти Result се стварају унутар петље и одмах постају отпад — они су идеални кандидати за Young GC. Објекат results живи дуже и мигрира у Old Generation. Подела генерација омогућава Minor GC-у да чисти младе објекте у милисекундама, не дирајући стару гомилу.
Android је прешао пут од Dalvik VM до ART (Android Runtime), а имплементација GC је једна од кључних разлика између њих. Разумевање архитектуре GC у Android-у помаже у писању кода који минимизира паузе на реалним уређајима.
| Карактеристика | Dalvik (до 4.4) | ART (5.0+) |
|---|---|---|
| Тип GC | Mark-and-Sweep са Concurrent Mark | Генерацијски + Concurrent |
| Типична пауза | 10–30 ms | 2–4 ms |
| Компактизација | Не (само фрагментација расте) | Да (u0443 позадини, без заустављања апликације) |
| AOT компилација | JIT (Just-In-Time) | AOT + JIT (хибрид) |
Dalvik је користио комбинацију Mark-and-Sweep са concurrent фазом. Concurrent Mark је омогућавао апликацији да настави рад током обиласка графа објеката, али је фаза Sweep захтевала заустављање свих нити (Stop-The-World). На уређајима са мало RAM (512 MB — 1 GB), паузе су досизале 30 ms, што је изазивало приметно успоравање интерфејса. Поред тога, Dalvik није компактизовао гомилу, па је након дужег рада фрагментација расла, а додељивање великих објеката (нпр. Bitmap) је могло да изазове OutOfMemoryError чак и уз довољну укупну слободну меморију.
ART (Android Runtime) је увела генерацијског сакупљача са concurrent компактизацијом. Гомиле су подељене на три региона: Young, Mature (аналог Old Generation) и Large Object Space (за објекте веће од 12 KB). Сакупљање Young региона се обавља паралелно без заустављања нити у већини случајева. У Android 10+ појавила се Concurrent Copying — компактизација се обавља у позадинској нити без Stop-The-World.
Захваљујући архитектури ART, типичне GC паузе су сведене на 2–4 ms, а у сценаријима са превлађу младих објеката — на 0.5–1 ms. Ово је омогућило Android уређајима да обезбеде стабилних 60 FPS чак и при активном раду са меморијом.
У Java екосистему постоји неколико GC имплементација, свака са сопственим профилом перформанси. За Android развој, избор је ограничен на ART, али је познавање Java GC корисно при писању серверског дела мобилних апликација и при развоју са Kotlin Multiplatform.
Serial GC — једнонитни сакупљач са потпуним заустављањем апликације (Stop-The-World). Свака операција Mark, Sweep и Compact се извршава једном нити. Перформансе је ниски — није примењив за мобилне сервере. Погодан само за мале апликације са гомилом до 100 MB.
Parallel GC (такође познат као Throughput Collector) користи више нити за све фазе сакупљања. Усмерен је ка максималном протоку (throughput) — минимизира време потрошено на GC у односу на време рада апликације. Укључује се преко заставице -XX:+UseParallelGC у JVM-у.
G1 (Garbage-First) GC — подразумевани сакупљач у Java 9+. Гомиле су подељене на регионе од 1–32 MB. G1 предвиђа време паузе и тежи да се уклопи у задати лимит (подразумевано 200 ms). Приоритет: прво се чисте региони са највећим обимом отпада (отатле и назив). G1 је ефикасан за сервере са великом гомилом (4–64 GB) и предвидивим паузама.
// Укључивање G1 GC са циљном паузом од 100 ms
// 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("Искоришћеност гомиле је премашила праг: " + used);
System.out.println("Размотрите смањење алокација");
}
}
}
Праћење гомиле кроз Runtime омогућава рано откривање цурења меморије. Ако used прелази 80% максималне гомиле при стабилном раду — то је сигнал за могуће цурење или прекомерну потрошњу меморије од стране апликације.
Чак и савремени ART GC не решава све проблеме — неправилно коришћење меморије остаје главни узрок jank-а и ANR (Application Not Responding). Размотримо основне сценарије и методе оптимизације.
GC Pauses — заустављања нити апликације током сакупљања. На екрану се манифестује како пропуштени оквири када време између два оквира прелази 16.6 ms (60 FPS). Ако GC траје 30 ms, црта се само један оквир уместо два — корисник види трзаје интерфејса.
Главни узроци дугих пауза: велики број живих објеката у Old Generation-у, фрагментација гомиле, чести Full GC. За дијагностику се користе Android Studio Profiler и systrace.
Главно правило GC-friendly кода — минимизирати број алоцираних објеката. Сваки нови објекат захтева не само додељивање меморије, већ и касније сакупљање. Чак ако је GC брз, 1000 сувишних алокација у секунди даје 1000 провера сакупљачу.
Цурење меморије настаје када објекат остаје доступан иако више није потребан. GC не може да уклони такав објекат, па се меморија постепено испразњује. Типични узроци: неодјављени слушаоци, статичке референце на Activity, анонимне класе које захватају спољни контекст и незатворени Cursor/InputStream.
// Цуреъе меморије: анонимна класа држи референцу на Activity
public void startTask() {
new Thread(new Runnable() { // имплицитно држи this (Activity)
@Override
public void run() {
// дуга операција...
System.out.println("Готово");
}
}).start();
}
// Исправка: статичка угљеждена класа + 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) {
// безбедан рад са Activity
}
}
}
У овом примеру, анонимни Runnable захвата имплицитну референцу на Activity. Док је нит жива — Activity не може бити покупљена од стране GC, чак ако је корисник већ затворио екран. Исправка WeakReference + static class прекида овај ланац и омогућава утилизацију Activity-ја.
Често постављана питања
GC у Android-у (ART) је генерацијски сакупљач са concurrent компактизацијом, оптимизован за мобилне уређаје са ограниченом меморијом. Java GC (G1, ZGC) су серверски сакупљачи са великим гомилама и предвидивим паузама. ART GC не користи JVM заставице — целокупно подешавање се обавља аутоматски на нивоу оперативног система.
Stop-The-World — тренутак када сакупљач зауставља све нити апликације да би безбедно прошао кроз граф објеката или ослободио меморију. Што је STW дужи, то је jank уочљивији. ART је скратио типично STW време на 2–4 ms захваљујући генерацијској архитектури.
Користите Android Studio Memory Profiler — приказује раст гомиле, број алокација и омогућава Heap Dump. За дубоку анализу користите LeakCanary — библиотека аутоматски открива цурења и приказује ланац референци који спречавају GC сакупљање.
Full GC — потпуно сакупљање свих генерација гомиле, укључујући Old Generation. У мобилним апликацијама Full GC може да траје 50–200 ms, изазивајући приметан jank или ANR. Главни узроци: фрагментација гомиле, цурење меморије, прекорачење прага Old Generation-а.
Kotlin пружа корутине са структурираном конкурентношћу — отказивање scope-а аутоматски отказује све подређене корутине, спречавајући цурења. Такође, у Kotlin-у постоји делегат lazy за лењиву иницијализацију и оператори области видљивости који смањују број привремених објеката.
Истаци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође