OutOfMemoryError — isang nakamamatay na exception na nangyayari kapag ang Java Virtual Machine (JVM) o Android Runtime (ART) ay hindi maka-allocate ng memory para sa isang bagong object dahil sa kakulangan ng espasyo sa Heap. Ayon sa Square Engineering, 70% ng OutOfMemoryError sa mga mobile application ay sanhi ng memory leaks, hindi aktwal na paglampas sa limitasyon. Pag-unawa sa mga sanhi ng OOM ay susi sa matatag na paggana ng aplikasyon.
Mga Pangunahing Punto
OutOfMemoryError (OOM) — isang exception mula sa pamilyang VirtualMachineError sa Java/Kotlin na nagpapahiwatig ng kawalan ng kakayahang mag-allocate ng memory para sa isang bagong object. Hindi tulad ng checked exceptions, ang OOM ay isang Error at hindi nangangailangan ng paghawak sa pamamagitan ng catch — kahit na teknikal na posibleng mahuli ito. Pagkatapos ng OOM, ang aplikasyon ay karaniwang nasa hindi matatag na estado at inirerekomenda na isara ito.
Sa Android, bawat aplikasyon ay may limitasyon ng Heap na itinakda ng gumawa ng device. Para sa mga modernong smartphone na may 6+ GB RAM, ang limitasyon ay 256–512 MB, para sa mga budget device — 128–192 MB. Kapag ang kabuuang dami ng lahat ng buhay na object ay lumampas sa limitasyong ito, ang ART ay nagtatapon ng OutOfMemoryError.
Mahalaga na maunawaan: ang OOM ay hindi palaging nangangahulugan na ang pisikal na memorya ng device ay naubos. Ito ay nangangahulugan na ang aplikasyon ay naubos na ang limitasyon ng Heap na itinakda ng system. Ang ibang mga aplikasyon ay maaaring may libreng memorya, ngunit ang iyong aplikasyon ay hindi ito magagamit dahil sa paghihiwalay ng proseso sa Android.
Limang senaryo ang regular na humahantong sa OOM sa mga mobile application. Bawat senaryo ay nauugnay sa isang partikular na uri ng data o operasyon.
Bitmap — pangunahing gumagamit ng memorya sa mga Android application. Ang pag-load ng FullHD na imahe (1920 × 1080) sa orihinal na laki ay kumukuha ng 8.3 MB sa ARGB_8888 format. Kung may 50 ganoong imahe sa RecyclerView — iyon ay 415 MB, na lumalampas sa Heap ng anumang device. Ang pag-load ng mga imahe nang walang inSampleSize — garantisadong OOM sa mahinang mga device.
Gumamit ng Glide o Coil para sa awtomatikong scaling. Ang mga library na ito ay naglo-load ng mga imahe sa laki na naaayon sa View, hindi sa orihinal na resolution. Para sa direktang paggamit ng BitmapFactory.Options, ilapat ang inSampleSize: kalkulahin ito bilang kapangyarihan ng dalawa upang ang huling laki ay hindi lalampas sa 2048 × 2048 pixels. Dagdag pa, gamitin ang RGB_565 sa halip na ARGB_8888 para sa mga imaheng walang transparency — ito ay nagbabawas ng konsumo ng memorya ng kalahati.
fun loadScaledBitmap(path: String, reqWidth: Int): Bitmap? {
val opts = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeFile(path, opts)
opts.inSampleSize = calculateSampleSize(opts.outWidth, reqWidth)
opts.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, opts)
}
Isang leak ng ilang KB ay hindi magiging sanhi ng OOM. Ngunit dose-dosenang leaks sa bawat screen ay naipon: bawat transition sa isang screen ay nagdaragdag ng leak, hindi mapalaya ng GC ang mga object, napupuno ang Heap. Karaniwang pattern: binubuksan at isinasara ng user ang profile screen nang 20 beses → ang Heap ay lumalaki ng 200 MB → ang aplikasyon ay nag-crash na may OOM.
I-install ang LeakCanary sa proyekto para sa awtomatikong pag-detect ng leaks. Ito ay magpapakita ng bawat tumutulong object na may tumpak na stack trace. Pagkatapos ayusin ang lahat ng leaks, ang konsumo ng Heap ay magiging matatag: pagkatapos isara ang screen, ang memorya ay babalik sa batayang antas.
Pag-load ng mga file nang buo sa byte[] — direktang daan patungo sa OOM. Ang isang JSON file na 50 MB sa pag-parse ay lilikha ng string ng parehong laki kasama ang DOM model. Ang mga video file na na-load sa memorya, audio buffer, at malalaking protobuf dataset — lahat ay maaaring lumampas sa limitasyon ng Heap sa isang operasyon.
Iproseso ang malalaking data gamit ang stream: InputStream na may buffer na 4–8 KB, streaming JSON parser (Jackson o Gson na may JsonReader), MediaCodec para sa video. Huwag kailanman tawagan ang File.readBytes() sa mga file na mas malaki sa 10% ng available na Heap.
Masinsinang paglikha ng mga object sa loop na walang intervening GC ay maaaring humantong sa OOM, lalo na sa mga device na may maliit na Heap. Halimbawa: pag-generate ng 100,000 object sa for-loop na hindi kasya sa Heap bago pa man makolekta ng GC ang mga ito. Ito ay mas karaniwan sa mga laro at graphic editor.
Gumamit ng Object Pool para sa mga object na mass na nilikha at winawasak. Para sa numerical data, gumamit ng primitive types (FloatArray sa halip na List<Float>). Ang RecyclerView na may ViewHolder Pool ay lumulutas ng problemang ito para sa mga UI component.
Fragmentasyon — estado kung saan ang libreng memorya ay sapat sa kabuuan, ngunit walang tuloy-tuloy na bloke para sa bagong object. Ang ART ay nag-compact ng Heap sa panahon ng GC, ngunit hindi palaging matagumpay. Ang malalaking array (Bitmap, byte[]) ay pinaka-sensitive sa fragmentation.
Ang ART sa Android 8+ ay gumagamit ng Generational GC, na nagbabawas ng fragmentation sa pamamagitan ng paghihiwalay ng mga bata at matandang object. Gayunpaman, iwasan ang pag-allocate ng mga fragment na may iba’t ibang laki sa iisang pool — subukang gumamit ng pre-allocated buffer na may nakapirming laki.
Ang limitasyon ng Heap sa Android ay hindi konstant — depende ito sa gumawa, modelo ng device, at bersyon ng OS. Ang Google ay nagtatakda ng mga minimum na kinakailangan sa pamamagitan ng Compatibility Definition Document (CDD), ngunit ang mga gumagawa ay nagtatakda ng aktwal na mga halaga.
| Kategorya ng Device | Karaniwang Heap | largeHeap |
|---|---|---|
| Budget (1–2 GB RAM) | 128–192 MB | 256–384 MB |
| Mid-range (3–4 GB RAM) | 256–384 MB | 512 MB |
| Flagship (6+ GB RAM) | 384–512 MB | 768 MB–1 GB |
| Tablet (4+ GB RAM) | 256–512 MB | 768 MB |
| Wear OS | 32–64 MB | Wala |
Maaaring humiling ng mas mataas na limitasyon sa pamamagitan ng android:largeHeap="true" sa manifest. Gamitin ito nang maingat: ang pagtaas ng Heap ay hindi lumulutas ng problema ng leaks at maaaring magpababa ng karanasan ng user kung ang sistema ay mapipilitang patayin ang ibang mga aplikasyon upang magbakante ng memorya. Para sa Wear OS, ang limitasyon ng Heap ay minimal — 32–64 MB lamang, dito ang largeHeap ay hindi available, at ang pagtitipid ng memorya ay dobleng kritikal.
Diagnosis ng OOM ay nangangailangan ng pagsusuri ng Heap Dump at pag-unawa kung aling mga object ang kumokonsumo ng memorya. Ang Android Studio ay nagbibigay ng lahat ng kinakailangang kasangkapan.
Hakbang 1: Huwag ang OOM moment. Sa Android Memory Profiler, pindutin ang Record memory allocations at isagawa ang senaryo na nagdudulot ng crash. Ang profiler ay magpapakita ng pagtalon ng mga alokasyon bago ang OOM. Kung hindi ma-reproduce ang OOM, bawasan ang Heap sa pamamagitan ng android:smallHeap sa debug build o gumamit ng DDMS na may manu-manong tawag sa GC.
Hakbang 2: Kumuha ng Heap Dump sa oras ng peak load (bago ang OOM). Buksan ang Dump sa Android Studio: ang tab na Classes ay naayos ayon sa Retained Size. Ang pinakamalaking object — Bitmap, byte[], String. Para sa bawat Bitmap, suriin ang laki (width × height × 4 bytes) at ang landas ng pag-load sa pamamagitan ng Stack Trace.
Hakbang 3: Suriin ang bilang ng mga umuulit na object. Kung makakita ka ng 200 magkaparehong Fragment o Activity — iyon ay leak. Kung 500 Bitmap na may parehong laki — problema sa cache ng imahe. Ang MAT (Memory Analyzer Tool) ay nagbibigay ng mas malalim na pagsusuri na may Dominator Tree, na nagpapakita kung aling mga object ang may hawak ng 80% ng Heap.
// Utos para sa Heap Dump sa pamamagitan ng adb
adb shell am dumpheap com.example.app /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof.
Isang komprehensibong estratehiya sa pag-iwas ng OOM ay may limang antas ng proteksyon: mula sa mga solusyong arkitektural hanggang sa pag-monitor sa produksyon.
ViewModel + Repository pattern ay naghihiwalay ng data mula sa UI at pumipigil sa paghawak ng View sa pag-ikot ng screen. Ang ViewModel ay nabubuhay nang mas mahaba kaysa sa Activity, ang data nito ay hindi nawawala, at ang View ay maaaring muling likhain nang walang pagdoble ng data sa memorya. Gumamit ng StateFlow sa halip na LiveData para sa malinaw na pamamahala ng estado.
Glide — sapilitang library para sa pagtatrabaho sa mga imahe. Ito ay awtomatikong nag-scale, nag-cache (disk + memorya), at nagre-recycle ng Bitmap. I-configure ang diskCacheStrategy at skipMemoryCache para sa malalaking listahan. Para sa mga animated na imahe, gumamit ng Glide na may GIF/WebP — sila ay kumukuha ng mas kaunting memorya kaysa sa isang sequence ng Bitmap.
Firebase Performance Monitoring ay sumusubaybay sa konsumo ng memorya sa real-time. Magtakda ng alert sa paggamit ng Heap na higit sa 80% ng limitasyon — ito ay senyales para sa pagsusuri. Ang Crashlytics ay nagtitipon ng OOM bilang exception at nagpapakita ng huling kilalang estado ng Heap bago ang crash. Para sa Android 11+, gamitin ang ApplicationExitInfo para sa detection ng OOM termination.
Sapilitang subukan ang aplikasyon sa mga device na may pinakamababang Heap (128–192 MB). Ang emulator na may maliit na screen at maliit na Heap ay ginagaya ang isang budget device. Kung ang aplikasyon ay gumagana sa naturang device, sa mga flagship ay walang magiging problema sa OOM. Gamitin ang Firebase Test Lab na may mga tunay na device mula sa iba’t ibang kategorya ng presyo.
// Pagsusuri ng available na Heap bago ang mabigat na operasyon
fun canAllocate(requiredBytes: Long): Boolean {
val runtime = Runtime.getRuntime()
val free = runtime.freeMemory()
return free > requiredBytes * 2 // reserba 50%
}
Mga Madalas Itanong
Teknikal na oo, ngunit hindi ito inirerekomenda. Pagkatapos ng OOM, ang aplikasyon ay nasa hindi matatag na estado: ang mga bagong alokasyon ay maaaring hindi gumana, at ang ilang mga object ay maaaring bahagyang nalikha. Ang tanging makatwirang aksyon sa catch — pag-log at pag-restart ng Activity.
Ang limitasyon ng Heap ay iba sa iba’t ibang device. Ang operasyon na nangangailangan ng 300 MB ay babagsak sa device na may limitasyon na 192 MB, ngunit gagana sa flagship na may 512 MB. Subukan sa mga device na may pinakamababang specs upang matuklasan ang mga OOM scenario.
largeHeap ay nagtataas ng limitasyon, ngunit hindi pinapabilis ang aplikasyon. Ang mga paghinto ng GC ay nagiging mas mahaba dahil ang pag-ipon ng malaking Heap ay mas matagal. Ang sistema ay maaaring pumatay ng mga background application upang magbigay ng memorya. Gamitin ang largeHeap lamang para sa mga aplikasyon na talagang nangangailangan ng maraming memorya (mga camera, editor).
OOM — exception sa loob ng aplikasyon kapag kulang ang Heap. Ang pagpatay ng system (Low Memory Killer) — desisyon ng Linux kernel na pumatay ng proseso upang magbakante ng memorya para sa ibang mga aplikasyon. Sa pagpatay ng system, ang aplikasyon ay hindi tumatanggap ng exception — ang proseso ay nagtatapos lamang.
Formula: width × height × bytesPerPixel. ARGB_8888 = 4 B/pixel, RGB_565 = 2 B/pixel. FullHD Bitmap (1920 × 1080) sa ARGB_8888 = 8.3 MB. 4K Bitmap (3840 × 2160) = 33 MB. Palaging i-scale ang mga imahe sa laki na kinakailangan para sa pagpapakita sa screen.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din