Kumakain ng memorya at namamaga — ano ito, mga sanhi at paano iwasan

May-akda: IT Sectr Nai-publish: 2026-07-28 Oras ng pagbabasa: 10 min

Memory leak — isa sa mga pinakamapanlinlang na problema sa mobile development. Ang memorya ng app ay patuloy na lumalaki hanggang sa maabot ang limitasyon na itinakda ng OS, pagkatapos ay magkakaroon ng OutOfMemoryError o sapilitang pagtatapos. Ayon sa Square Engineering, halos 40% ng mga Android app ay may kahit isang memory leak na makikita lamang sa profiling. Talakayin natin ang mga sanhi at paraan ng pagpigil sa paglaki ng memorya.

Mga pangunahing punto

  • GC reachability — hindi natatanggal ang object kung may aktibong reference mula sa root set
  • Static na mga reference sa Activity o Context — pinakakaraniwang sanhi ng leak sa Android
  • LeakCanary — karaniwang tool para sa awtomatikong pagtuklas ng leak sa Android
  • WeakReference — solusyon para sa mga reference na hindi dapat hadlangan ang garbage collection
  • Mga Lifecycle-aware na component awtomatikong kinakansela ang mga subscription kapag nawasak ang view

Ano ang memory leak at pamamaga ng app?

Memory leak — sitwasyon kung saan ang isang object na hindi na kailangan ng app ay nananatili sa heap dahil may aktibong reference mula sa root set (GC Root). Itinuturing ng garbage collector na buhay ang naturang object at hindi ito tinatanggal.

Pamamaga ng memorya (memory bloat) — mas malawak na problema kapag ang app ay kumokonsumo ng mas maraming memorya kaysa kinakailangan para sa kasalukuyang mga gawain. Mga sanhi: labis na caching, pagdoble ng mga object, hindi optimal na mga istruktura ng data, at fragmentation ng heap.

Sa Android bawat app ay inilalaan ng limitadong heap (karaniwang 64-512 MB depende sa device at bersyon ng OS). Sa iOS ang limitasyon ay hindi gaanong mahigpit, ngunit ang system ay nagpapadala ng babala sa memorya kapag papalapit sa limitasyon.

KatangianAndroidiOS
Limitasyon ng heap64-512 MB (depende sa device)Implicit (system)
Garbage collectionART (Concurrent, Compact)ARC (Automatic Reference Counting)
Mekanismo ng leakGC Root referencesRetain cycles (malakas na reference cycles)
ResultaOutOfMemoryErrorBabala sa memorya → pagtatapos

Ayon sa Facebook Engineering Blog, ang memory leak ay sanhi ng ~15% ng mga crash report sa mobile app. Sa Android, idinagdag ang ANR dahil sa madalas na GC pause kapag kulang sa memorya.

Mga tipikal na pattern ng memory leak sa Android at iOS

Static na reference sa Activity — klasiko ng Android leaks. Kung ang isang static na field o singleton ay nag-iimbak ng reference sa Activity, hindi ito kokolektahin ng GC kahit pagkatapos ng finish(), habang buhay ang singleton. Ang Activity ay isang mabigat na object na naglalaman ng View hierarchy, resources, at Context.

kotlin
object LeakHolder {
    var activityRef: Activity ?= null // leak: static na reference sa Activity
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle ?= null) {
        super.onCreate(savedInstanceState)
        LeakHolder.activityRef = this // ❌ Ang MainActivity ay hindi kailanman makokolekta ng GC
    }
}

Mga anonymous na klase at lambda — implicitly na nag-iimbak ng reference sa panlabas na klase. Kung ang Runnable o Callback ay ipinasa sa panlabas na serbisyo at ang Activity ay nawasak, ang anonymous class object ay nakabitin pa rin sa queue at hindi pinapayagan ang Activity na ma-garbage collect.

  • Handler na may delay — kung nawasak ang Activity ngunit hindi pa naisakatuparan ang Handler.postDelayed, tumutulo ang Activity
  • Thread at AsyncTask — sa pag-ikot ng screen, muling nilikha ang Activity, ngunit ang lumang Thread ay patuloy na humahawak ng reference sa lumang Activity
  • Retrofit/Callback — isang anonymous na Callback ay nag-iimbak ng reference sa presenter o fragment
  • Mga tagamasid (Observers) — LiveData o RxJava subscription nang walang pag-kansela sa onDestroy

Sa iOS ang pangunahing problema ay retain cycles: dalawang object ang nag-iimbak ng malalakas na reference sa isa't isa at hindi mababawasan ng ARC ang reference count para sa alinman. Karaniwang kaso: isang closure na malakas na kumukuha ng self, at self na may hawak na reference sa closure.

Paano matukoy ang mga memory leak?

LeakCanary — library mula sa Square para sa awtomatikong pagtuklas ng leak sa Android. Pagkatapos ng pagkasira ng Activity o Fragment, sinusuri nito kung ang object ay nakolekta ng GC. Kung hindi — gumagawa ito ng heap dump at nagpapakita ng leak trace.

kotlin
// LeakCanary 2.x — auto-integration sa pamamagitan ng Application
class ExampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // Awtomatikong nag-i-install ang LeakCanary sa debug build
        // sa pamamagitan ng ContentProvider — zero code setup
    }
}

// Sapilitang tawag sa pagsusuri
AppWatcher.objectWatcher.watch(watchedObject, "leak description")

Android Studio Profiler — built-in na tool para sa real-time na pagsubaybay sa memorya. Nagbibigay-daan sa pag-record ng heap dump, paghahanap ng mga kahina-hinalang object (Retained Size > 1 MB), at pagsubaybay sa GC root path papunta sa bawat object.

Para sa iOS gamitin ang Xcode Memory Graph Debugger. Binibigyang-diin nito ang graph ng mga object sa memorya, ipinapakita ang retain cycles at pinapayagan ang agarang pagtuklas ng mga circular reference. Available din ang Instruments > Allocations para sa pangmatagalang pagsubaybay.

Mga estratehiya sa pagpigil ng leak

WeakReference — pangunahing mekanismo para sa mga reference na hindi dapat hadlangan ang garbage collection. Kung magpasya ang GC na tanggalin ang object, ang WeakReference ay nagbabalik ng null. Ginagamit para sa mga callback, listener, at reference sa mga UI component mula sa mga background thread.

Mga Lifecycle-aware na component — arkitektural na diskarte na ipinatupad sa Android Jetpack (Lifecycle, LiveData, Flow, coroutines). Awtomatikong kinakansela ang mga subscription sa onDestroy, na inaalis ang pangunahing klase ng leaks.

kotlin
class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<List<User>>()
    val data: LiveData<List<User>> get() = _data

    fun loadData() {
        viewModelScope.launch {
            val result = repository.fetchData()
            _data.postValue(result)
            // awtomatikong kinakansela ang coroutine sa onCleared()
        }
    }
}

viewModelScope at lifecycleScope — built-in na CoroutineScope sa Android na kinakansela sa kaukulang lifecycle event. Inaalis nito ang mga leak sa pamamagitan ng coroutine — ang pinakakaraniwang senaryo sa modernong Android development.

  • Huwag gumamit ng static na reference sa Context, Activity, View o Fragment
  • Kanselahin ang lahat ng RxJava subscription sa disposeBag / CompositeDisposable sa onDestroy
  • Gamitin ang [weak self] / [unowned self] sa iOS closures para maiwasan ang retain cycles
  • Suriin ang Bitmap at malalaking object — dapat i-recycle o i-null ang mga ito

Mga tool sa profiling ng memorya

Memory Profiler in Android Studio — pangunahing tool para sa pagsubaybay sa heap. Nagpapakita ng live allocations, heap snapshot, bilang ng mga object ayon sa uri. Pinapayagan ang pag-record ng dump at pagsusuri nito sa MAT (Memory Analyzer Tool) para sa paghahanap ng mga kahina-hinalang object.

Eclipse MAT — desktop analyzer ng heap dump. Pagkatapos mag-load ng HPROF file mula sa Android Studio, nagtatayo ang MAT ng dominator tree, ipinapakita ang retain size ng bawat object, at nag-aalok ng awtomatikong pagsusuri ng mga kahina-hinalang leak sa pamamagitan ng Leak Suspects Report.

Xcode Memory Graph — visual debugger para sa retain cycles. Sa pag-click sa Memory Graph Debugger button, itinitigil ng Xcode ang app, nagtatayo ng kumpletong graph ng mga object sa memorya, at itinatampok ang retain cycles sa pula.

ToolPlatformKatangian
LeakCanaryAndroidAwtomatikong pagtuklas ng leak pagkatapos ng destroy
Memory ProfilerAndroid StudioHeap dump + live allocations
Eclipse MATAndroidDominator tree, Leak Suspects Report
Memory GraphiOS (Xcode)Visualisador ng retain cycles

Ayon sa Google I/O 2023, ang mga app na gumagamit ng LeakCanary sa debug build ay nakakabawas ng bilang ng memory-related crash ng 30-50% sa unang 2 buwan pagkatapos ng implementasyon. Inirerekomenda na idagdag ang LeakCanary sa onboarding phase ng proyekto.

Mga madalas itanong

Ano ang pagkakaiba ng memory leak at pamamaga?

Leak — mga object na hindi naa-access ng code ngunit hindi tinatanggal ng GC dahil sa aktibong reference. Pamamaga — ang app ay nag-iimbak ng mga object na lohikal na kailangan ngunit sa sobrang dami (hal. 50 MB cache sa isang tumatakbong app na 80 MB). Ang pamamaga ay ginagamot sa arkitektura, ang leak — sa pamamagitan ng tamang pamamahala ng reference.

Paano nakakahanap ng leak ang LeakCanary?

LeakCanary ay gumagamit ng ObjectWatcher — pagkatapos ng onDestroy() Activity, lumilikha ito ng WeakReference sa Activity at pinapatakbo ang GC. Kung pagkatapos ng 5 segundo ang WeakReference ay hindi nalinis, gumagawa ang LeakCanary ng heap dump, sinusuri ang pinakamaikling reference chain mula GC Root hanggang object, at ipinapakita ang eksaktong leak stack na may pangalan ng file at linya ng code.

Bakit ang Bitmap ay madalas na nagdudulot ng OutOfMemoryError?

Bitmap ay gumagamit ng memorya sa labas ng Java heap sa native memory (native heap). Laki ng isang Bitmap = lapad × taas × 4 bytes (ARGB_8888). Ang 12 MP na larawan (4000×3000) ay sumasakop ng 48 MB. Hindi laging maaaring palayain ng Android ang native memory sa tamang oras, na kapag naipon ang maraming Bitmap ay humahantong sa OOM kahit na may sapat na Java heap.

Ano ang retain cycle sa iOS?

Retain cycle — sitwasyon sa ARC kung saan dalawang object ang nag-iimbak ng malalakas na reference sa isa't isa at ang reference count ay hindi kailanman umaabot sa zero. Karaniwang halimbawa: ViewController na may malakas na reference sa closure, at closure na malakas na kumukuha ng self. Solusyon: gumamit ng [weak self] o [unowned self] sa mga closure.

Ano ang maximum na laki ng heap sa Android?

Ang laki ng heap ay depende sa device at bersyon ng Android. Para sa lumang device (API 15-24) — 64-128 MB. Para sa modernong device (API 25+) — 256-512 MB. Ang eksaktong halaga ay makukuha sa pamamagitan ng ActivityManager.getMemoryClass(). Para sa malalaking app (laro, editor) mayroong largeHeap=true sa manifest, na nagbibigay ng hanggang 1 GB.

Buod

  • Memory leak — object na hindi tinatanggal ng GC dahil sa aktibong reference mula sa root set; pamamaga — labis na pagkonsumo ng memorya nang walang malinaw na leak
  • Static na reference sa Activity, Context, View — numero unong sanhi ng leak sa Android; solusyon — WeakReference o Application Context
  • Anonymous na klase at lambda ay implicitly na nag-iimbak ng reference sa panlabas na klase; hindi nakanselang callback — pangalawang pinakakaraniwang sanhi
  • LeakCanary — pamantayan ng awtomatikong pagtuklas ng leak sa Android; ang integration ay tumatagal ng 5 minuto at nagpapababa ng crash rate ng 30-50%
  • lifecycleScope at viewModelScope ay awtomatikong kinakansela ang coroutine sa destroy, inaalis ang buong klase ng leaks
  • Retain cycles sa iOS ay nalulutas sa pamamagitan ng weak/unowned self sa closures at delegates
  • Profile ang memorya kahit isang beses bawat sprint — heap dump na may MAT o Memory Graph ay dapat bahagi ng code review

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.

Pag-usapan ang proyekto

Basahin din