موبائل ایپلیکیشنز میں میموری لیک — یہ کیا ہے، وجوہات اور پتہ لگانے کے طریقے

مصنف: IT Sectr اشاعت: 2026-03-29 مطالعے کا وقت: 9 منٹ

میموری لیک (Memory Leak) ایک ایسی صورت حال ہے جس میں ایپلیکیشن ان اشیاء کے حوالہ جات رکھتی ہے جن کی مزید ضرورت نہیں، جس سے کوڑا کرکٹ جمع کرنے والا (GC) قبضہ شدہ میموری کو آزاد نہیں کر پاتا۔ LeakCanary کے مطابق، اچھی طرح لکھی گئی ایپلیکیشنز میں بھی ہر 10,000 لائن کوڈ میں 3–5 لیک پائے جاتے ہیں۔ ہر لیک دستیاب میموری کو آہستہ آہستہ کم کرتا ہے، جس سے سستی اور OutOfMemoryError پیدا ہوتا ہے۔

اہم نکات

  • Memory Leak — ایک شے میموری میں رہتی ہے حالانکہ ایپلیکیشن منطق سے اس کا کوئی فعال حوالہ نہیں
  • Activity یا Context کے جامد حوالہ جات — Android میں لیک کی سب سے عام وجہ
  • LeakCanary — Android میں خودکار لیک پتہ لگانے کا معیاری آلہ
  • WeakReference اور Application Context — لیک روکنے کی بنیادی تکنیک
  • Lifecycle-aware اجزاء سبسکرپشن سے متعلق لیک کی پوری کلاس کو ختم کرتے ہیں

میموری لیک کیا ہے

میموری لیک (Memory Leak) ایک ایسی صورت حال ہے جس میں ایک شے مضبوط حوالہ جات (Strong Reference) کی زنجیر کے ذریعے قابل رسائی رہتی ہے، حالانکہ یہ منطقی طور پر ایپلیکیشن کے لیے مزید ضروری نہیں۔ کوڑا کرکٹ جمع کرنے والا (GC) ایسی شے کو زندہ سمجھتا ہے اور اس کے قبضہ کردہ میموری کو آزاد نہیں کرتا۔ نتیجے کے طور پر، دستیاب ہیپ میموری مسلسل کم ہوتی ہے اور GC توقف کی تعدد بڑھ جاتی ہے۔

دستی میموری انتظام والی زبانوں (C, C++) کے برعکس، Java/Kotlin میں لیک بھولا ہوا free() نہیں بلکہ بھولا ہوا حوالہ ہے۔ جب تک GC روٹ سے لیک شدہ شے تک مضبوط حوالہ موجود ہے، GC اسے ضروری سمجھتا ہے۔ عام GC روٹس: جامد فیلڈز، فعال تھریڈز، کال اسٹیک، JNI عالمی حوالہ جات۔

لیک کا خطرہ ان کا جمع ہونے والا اثر ہے۔ ایک 100 KB کا لیک قابل توجہ نہیں، لیکن اس طرح کے 100 لیک 10 MB گھیر لیتے ہیں اور ایپلیکیشن بار بار GC کی وجہ سے سست ہونے لگتی ہے۔ لیک کی اہم مقدار OutOfMemoryError اور ایپلیکیشن کے کریش کا باعث بنتی ہے۔ لیک کی علامات: Profiler گراف پر میموری کی کھپت میں مسلسل اضافہ، STW (Stop The World) کے ساتھ بار بار GC توقف، اور UI کارکردگی میں تنزلی۔

موبائل ایپلیکیشنز میں لیک کی عام اقسام

پانچ اقسام کے لیک موبائل ڈویلپمنٹ میں 95% کیسز کو کور کرتے ہیں۔ ہر ایک کی اپنی وجہ اور کوڈ میں خصوصی پیٹرن ہے۔

Activity یا Context کا جامد حوالہ

سب سے معروف Android لیک Activity یا Context کا جامد حوالہ ذخیرہ کرنا ہے۔ عام کوڈ: ایک جامد Activity فیلڈ جو onDestroy() میں null نہیں کی جاتی۔ جب تک جامد فیلڈ زندہ ہے، پوری Activity اپنے View ٹری کے ساتھ زندہ رہتی ہے، جو 1–10 MB گھیر سکتی ہے۔ یہ کلاسک لیک ہے جسے LeakCanary سب سے پہلے ڈھونڈتا ہے۔

حل: Activity یا Context کو کبھی جامد فیلڈز میں ذخیرہ نہ کریں۔ Activity سے زیادہ زندہ رہنے والے سنگلٹن کے لیے Application Context استعمال کریں۔ اگر Activity کا حوالہ درکار ہے تو WeakReference<Activity> استعمال کریں۔

kotlin
object MySingleton {
    private var weakActivity: WeakReference<Activity>? = null

    fun attach(activity: Activity) {
        weakActivity = WeakReference(activity)
    }
}

مضمر حوالہ والی داخلی کلاسیں

گمنام کلاسیں اور غیر جامد داخلی کلاسیں مضمر طور پر شامل کرنے والی کلاس کا حوالہ رکھتی ہیں۔ Runnable جو Handler کو دیا جاتا ہے اور onDestroy() کے بعد عمل میں آتا ہے، پوری Activity کو روکے رکھتا ہے۔ Activity کو کیپچر کرنے والا Retrofit کال بیک بھی ایسا ہی کرتا ہے۔ یہ لیک کی سب سے خطرناک قسم ہے — مضمر حوالہ کوڈ میں نظر نہیں آتا۔

Kotlin object اظہار اور لیمبڈا بھی بیرونی کلاس کے حوالہ جات کیپچر کرتے ہیں۔ داخلی کلاسوں کو جامد (یا Kotlin میں ٹاپ لیول) بنائیں اور بیرونی حوالہ جات WeakReference کے ذریعے بھیجیں۔ لیمبڈا کے لیے viewLifecycleOwner کے ساتھ Lifecycle-aware نقطہ نظر استعمال کریں۔

غیر منسوخ کردہ سننے والے اور سبسکرپشنز

سسٹم سروسز کو سبسکرائب کرنا بغیر منسوخ کیے — یہ براہ راست لیک ہے۔ SensorManager، LocationManager، NotificationListener جو onResume() میں رجسٹرڈ ہیں اور onPause() میں unregister نہیں کہتے، Activity کو روکے رکھتے ہیں۔ اسی طرح: RxJava Disposable جو CompositeDisposable میں شامل نہیں، اور GlobalScope کے ذریعے شروع کیا گیا coroutine۔

Lifecycle-aware اجزاء استعمال کریں: LifecycleOwner کے ساتھ observe() onDestroy() پر خود بخود سبسکرپشن منسوخ کرتا ہے۔ RxJava کے لیے — DisposableObserver کے ساتھ viewLifecycleOwner.lifecycle.addObserver۔ coroutine کے لیے — lifecycleScope.launch() زندگی کے دور سے منسلک ہوتا ہے۔

kotlin
// Lifecycle کے ذریعے خودکار سبسکرپشن منسوخی
viewModel.userData.observe(viewLifecycleOwner) { data ->
    updateUI(data)
}

// lifecycleScope کے ساتھ coroutine
lifecycleScope.launch {
    viewModel.loadData().collect { render(it) }
}

recycle کے بغیر Bitmap

Bitmap ہیپ میموری کی ایک خاصی مقدار گھیرتا ہے: ایک FullHD بٹ میپ 1920 × 1080 × 4 بائٹ = 8.3 MB ہے۔ اگر فہرست کے ہر آئٹم کے لیے Bitmap بنایا جائے اور چھپانے پر recycle() نہ بلایا جائے تو میموری جلدی ختم ہو جاتی ہے۔ Android کے پرانے ورژن (3.0 سے پہلے) میں Bitmap مقامی میموری میں محفوظ ہوتا تھا، لیکن جدید ورژن میں یہ Dalvik/ART ہیپ میں ہے، اور GC اسے صرف اس وقت آزاد کر سکتا ہے جب مضبوط حوالہ نہ ہو۔

تصاویر لوڈ کرنے کے لیے Glide یا Coil استعمال کریں — یہ لائبریریاں کیشنگ اور ری سائیکلنگ کو خود بخود منظم کرتی ہیں۔ اگر براہ راست Bitmap کے ساتھ کام کر رہے ہیں تو بڑی تصاویر کے لیے bitmap.recycle() کال کریں جو مزید ڈسپلے نہیں ہو رہی ہیں، اور چھوٹی کاپیاں لوڈ کرنے کے لیے inSampleSize استعمال کریں۔

onDestroyView کے بعد Fragment حوالہ

Fragment کے دو زندگی کے دور ہیں: خود Fragment کا اور اس کے View کا۔ onDestroyView() کے بعد View ٹری تباہ ہو جاتا ہے، لیکن اگر بیرونی حوالہ ہو تو خود Fragment میموری میں رہ سکتا ہے۔ ایک عام غلطی ViewPager اڈاپٹر یا نیویگیشن گراف میں Fragment کا حوالہ ذخیرہ کرنا ہے جو تباہ ہونے پر صاف نہیں ہوتا۔

طویل عمر والی اشیاء کے فیلڈز میں Fragment کا حوالہ کبھی ذخیرہ نہ کریں۔ نیسٹڈ fragment کے لیے childFragmentManager اور ان کے درمیان ڈیٹا کی منتقلی کے لیے LifecycleOwner کے ساتھ observe() استعمال کریں۔ ViewPager2 نے اس مسئلے کو API سطح پر حل کیا: FragmentTransactionAdapter زندگی کے دور کو صحیح طریقے سے منظم کرتا ہے۔

میموری لیک کا پتہ کیسے لگائیں

لیک کا پتہ لگانے کے لیے دو حقائق کی تصدیق ضروری ہے: متوقع زندگی کے بعد میموری واپس نہیں آتی اور ایک مخصوص قسم کی اشیاء کی تعداد بغیر کم ہوئے بڑھتی ہے۔ تشخیصی عمل تین مراحل پر مشتمل ہے۔

پہلا مرحلہ — Android Studio میں Memory Profiler کے ذریعے بصری جانچ۔ Memory ٹیب کھولیں، ہدفی کارروائی کریں (اسکرین کھولیں اور بند کریں)، GC (Garbage Collection) دبائیں اور دیکھیں کہ آیا میموری ابتدائی سطح پر واپس آتی ہے۔ اگر 3–4 کھولنے بند کرنے کے چکروں کے بعد میموری مسلسل بڑھتی ہے — لیک ہے۔

دوسرا مرحلہ — Heap Dump لینا۔ Memory Profiler میں Dump Java Heap دبائیں۔ نتیجے میں آنے والی .hprof فائل Android Studio میں کھولیں: آپ ہیپ میں تمام اشیاء کو سائز اور حوالہ جات کے ساتھ دیکھیں گے۔ ان کلاسوں کو تلاش کریں جن کی تعداد اسکرین بند کرنے کے بعد صفر ہونی چاہیے۔ مثال کے طور پر، بند کرنے کے بعد تعداد 2 والی MainActivity — واضح لیک ہے۔

تیسرا مرحلہ — Retained Size اور GC Root کا تجزیہ۔ Android Studio میں Retained Size کا تجزیہ کریں: اگر آپ اس شے کو ہٹاتے ہیں تو کتنی میموری آزاد ہوگی۔ GC Root سے شے کا راستہ دکھاتا ہے کہ اسے کیا روکے ہوئے ہے: Static field → HashMap → Activity — اور آپ لیک پوائنٹ دیکھتے ہیں۔ Reference ویجیٹ پینل شے کے تمام حاملین کو دکھاتا ہے۔

لیک تلاش کرنے کے اوزار

چار اوزار خودکار پتہ لگانے سے لے کر گہرائی سے Heap Dump تجزیہ تک لیک کی تلاش کو کور کرتے ہیں۔

آلہطریقہنتائج کی شکل
LeakCanaryخودکار نگرانیHeap Dump + لیک stack trace
Android Memory Profilerدستی نگرانیمیموری گراف + Heap Dump
MAT (Eclipse)گہرائی سے تجزیہDominator Tree رپورٹ + GC Root راستہ
Perfettoسسٹم وسیع ٹریسنگٹائم لائن + مقامی میموری

LeakCanary کسی بھی Android پروجیکٹ کے لیے ناگزیر ہے۔ یہ Activity/Fragment کے زندگی کے دور کے اختتام پر خود بخود لیک کا پتہ لگاتا ہے اور stack trace کے ساتھ درست لیک مقام دکھاتا ہے۔ انضمام: build.gradle میں ایک لائن۔ LeakCanary 2.x کو دستی ابتدا کی ضرورت نہیں — یہ خود بخود Application Watcher رجسٹر کرتا ہے۔

میموری لیک کو کیسے روکیں

لیک کی روک تھام قواعد اور اوزاروں کے ایک سیٹ کے ذریعے ترقیاتی عمل میں شامل کی جاتی ہے جو ہر مرحلے پر کوڈ کی جانچ کرتے ہیں۔

مضبوط حوالہ کا اصول

Activity، Fragment یا View کا حوالہ کبھی جامد فیلڈ، سنگلٹن یا طویل عمر والی شے میں ذخیرہ نہ کریں۔ اگر حوالہ ناگزیر ہے تو WeakReference استعمال کریں یا ViewModel کے ذریعے ڈیٹا ذخیرہ کریں، جو بالکل اتنی دیر زندہ رہتا ہے جتنا ضروری ہے اور براہ راست View کو نہیں رکھتا۔

Lifecycle-aware فن تعمیر

Android Architecture Components سے ViewModel اور LiveData فن تعمیر کی سطح پر زندگی کے دور کے مسئلے کو حل کرتے ہیں۔ ViewModel اسکرین گردش سے بچ جاتا ہے اور اس میں View کے حوالہ جات نہیں ہوتے۔ LiveData onDestroy() پر خود بخود مبصر کی سبسکرپشن منسوخ کرتا ہے۔ سسٹم سروسز کی دستی سبسکرپشن کے بجائے ان کا استعمال کریں۔

GC Root پر مرکوز کوڈ کا جائزہ

کوڈ کے جائزے میں توجہ دیں: Context/View اقسام والے جامد فیلڈز، گمنام کلاسیں، Activity کیپچر کرنے والے لیمبڈا، دستی سبسکرپشنز، composite کے بغیر RxJava disposable، Bundle کے ذریعے Fragment ذخیرہ کرنا۔ Kotlin میں، زندگی کے دور کی پابندی کے بغیر launch والے coroutine کو اضافی طور پر چیک کریں۔

CI میں خودکار جانچ

LeakCanary ٹیسٹ پائپ لائن کے حصے کے طور پر کام کر سکتا ہے: LeakCanary کے ساتھ قبولیتی ٹیسٹ چلائیں اور اگر لیک ملے تو بلڈ ناکام کر دیں۔ یہ لیک کو پروڈکشن تک پہنچنے سے روکتا ہے۔ Android Lint کے StaticFieldLeak اصول کے ساتھ جانچ کو مکمل کریں — یہ جامد تجزیہ کی سطح پر ممکنہ لیک ڈھونڈتا ہے۔

kotlin
// ٹیسٹوں میں LeakCanary
class LeakTest {
    @Test
    fun activityShouldNotLeak() {
        ActivityScenario.launch(MainActivity::class.java)
            .close()
        LeakAssertions.assertNoLeak() // لیک ہونے پر ناکام
    }
}

اکثر پوچھے گئے سوالات

میموری لیک OutOfMemoryError سے کیسے مختلف ہے؟

لیک وجہ ہے، اور OutOfMemoryError نتیجہ ہے۔ ایک لیک OOM کا باعث نہیں بنتا، لیکن درجنوں لیک کا جمع ہونا Heap کو ختم کر دیتا ہے۔ OOM ایک مہلک استثناء ہے، جبکہ لیک ایک پیٹرن ہے جو وقت کے ساتھ اس کی طرف لے جاتا ہے۔

LeakCanary کے بغیر لیک کیسے تلاش کریں؟

Android Memory Profiler کے ذریعے: اسکرین کو 5 بار کھولیں اور بند کریں، ہر بند کرنے کے بعد GC کو کال کریں۔ اگر میموری بنیادی سطح پر واپس نہیں آتی — لیک ہے۔ Heap Dump لیں اور فہرست میں وہ Activity کلاس تلاش کریں جس کی تعداد بند کرنے کے بعد 0 سے زیادہ ہے۔

کیا Kotlin زبان کی سطح پر لیک روک سکتا ہے؟

جزوی طور پر۔ Kotlin null-safety مسئلہ حل کرتا ہے لیکن مضبوط حوالہ جات کا انتظام نہیں کرتا۔ lifecycleScope اور viewModelScope والے coroutine پس منظر کے کاموں سے لیک روکتے ہیں، جبکہ sealed class اور data class لیک کی طرف لے جانے والی حالتوں کی تعداد کم کرتے ہیں۔ بنیادی تحفظ زبان کی خصوصیات نہیں بلکہ آرکیٹیکچرل پیٹرن ہیں۔

LeakCanary ایسا لیک کیوں ڈھونڈتا ہے جو موجود نہیں؟

LeakCanary کبھی کبھی غلط مثبت نتائج دیتا ہے: ایک شے عارضی طور پر سسٹم کے ذریعے روکی جا سکتی ہے (مثال کے طور پر، InputMethodManager آخری View کو روکتا ہے)۔ دستی طور پر چیک کریں: اگر Retained Size < 1 KB ہے اور GC Root ایک سسٹم سروس ہے، تو یہ ممکنہ طور پر غلط مثبت ہے۔

کیا میموری لیک صرف Android پر ہوتی ہے؟

نہیں۔ لیک کسی بھی پلیٹ فارم پر ممکن ہے جس میں GC ہو: iOS (Swift/Objective-C)، Flutter (Dart)، ویب براؤزر (JavaScript)۔ میکانزم ایک جیسے ہیں — GC Root سے مضبوط حوالہ۔ iOS پر، ARC خود بخود میموری کا انتظام کرتا ہے، لیکن اشیاء کے درمیان retain cycle اسی لیک کو پیدا کرتا ہے۔

خلاصہ

  • Memory Leak — ایک شے جسے GC بھولے ہوئے مضبوط حوالہ کی وجہ سے آزاد نہیں کر سکتا
  • Activity اور Context کے جامد حوالہ جات — لیک کی سب سے عام وجہ
  • مضمر حوالہ جات (گمنام کلاسیں، لیمبڈا اور RxJava سبسکرپشنز) واضح سے زیادہ خطرناک
  • LeakCanary خود بخود لیک ڈھونڈتا ہے اور درست stack trace دکھاتا ہے
  • Lifecycle-aware اجزاء (ViewModel, LiveData, lifecycleScope) لیک کی ایک کلاس ختم کرتے ہیں
  • Heap Dump اور Retained Size تجزیہ — دستی تشخیص کا اہم طریقہ
  • روک تھام میں مضبوط حوالہ جات پر مرکوز کوڈ کا جائزہ اور LeakCanary کے ساتھ CI جانچ شامل ہے

ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے

IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔

پروجیکٹ پر بحث کریں

مزید پڑھیں