میموری لیک: یہ کیا ہے، عام منظرنامے اور تشخیص

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

میموری لیک (memory leak) — ایک ایسی صورت حال جہاں ایپلیکیشن ان اشیاء کے زیر قبضہ میموری کو آزاد نہیں کرتی جن کی مزید ضرورت نہیں ہے۔ موبائل ڈویلپمنٹ میں یہ خاص طور پر اہم ہے: محدود heap اور swap کی عدم موجودگی OutOfMemoryError اور ایپلیکیشن کے کریش کا سبب بنتی ہے۔ Purdue University (2022) کے مطابق، Google Play پر 35% Android ایپلیکیشنز میں کم از کم ایک میموری لیک ہوتا ہے۔ آئیے عام منظرناموں، تشخیصی ٹولز اور حل کے طریقوں کا جائزہ لیتے ہیں۔

اہم نکات

  • GC Root — داخلے کا نقطہ جس کے ذریعے کوڑا کرکٹ جمع کرنے والا زندہ اشیاء کا تعین کرتا ہے
  • Context لیک — سنگلٹن میں Activity Context منتقل کرنا پورے View درجہ بندی کو روک لیتا ہے
  • Handler postDelayed کے ساتھ — اگر Activity تباہ ہو جائے، Handler اسے GC پر جانے سے روکتا ہے
  • Heap dump — MAT یا Android Profiler کے ذریعے لیک تجزیہ کا بنیادی طریقہ
  • SoftReference — میموری کی کمی پر خودکار صفائی والے کیشز کے لیے WeakReference کا متبادل

موبائل ایپلیکیشنز میں میموری لیک کیا ہے؟

میموری لیک — ایک ایسی صورت حال جہاں مختص کردہ میموری پروگرام کے آبجیکٹ کی مزید ضرورت نہ ہونے کے بعد بھی سسٹم کو واپس نہیں کی جاتی۔ کوڑا کرکٹ جمع کرنے والا اس آبجیکٹ کو زندہ سمجھتا ہے کیونکہ GC Root سے ایک فعال حوالہ زنجیر اس کی طرف اشارہ کر رہی ہے۔

Java/Kotlin میں، کوڑا کرکٹ جمع کرنے والا خود بخود کام کرتا ہے، لیکن یہ تعین نہیں کر سکتا کہ کوئی آبجیکٹ منطقی طور پر غیر ضروری ہے اگر اس کا تکنیکی حوالہ موجود ہو۔ ڈویلپر کو غیر ضروری رابطوں کو واضح طور پر توڑنا چاہیے۔ Swift/Objective-C میں، ARC خود بخود حوالوں کو گنتا ہے، لیکن retain cycles کاؤنٹر کو صفر ہونے سے روکتی ہیں۔

لیک کا بنیادی خطرہ مجموعی اثر ہے۔ ہر لیک تھوڑی مقدار میں میموری استعمال کرتا ہے، لیکن بار بار اسکرین ٹرانزیشن (اسکرین گھماؤ، Activity کھلنا/بند ہونا) کے ساتھ لیک جمع ہوتے رہتے ہیں جب تک کہ heap کی حد ختم نہ ہو جائے۔

لیک بلاٹ سے کیسے مختلف ہے؟

لیک — آبجیکٹ کوڈ کے لیے ناقابل رسائی ہے لیکن GC کے ذریعے ہٹایا نہیں گیا۔ بلاٹ — آبجیکٹ منطقی طور پر ضروری ہے لیکن ضرورت سے زیادہ مقدار میں محفوظ ہے۔ بلاٹ کی مثال: 30 MB کے ورکنگ سیٹ کے ساتھ 100 MB کی تصویر کیش۔ دونوں مسائل OOM کی طرف لے جاتے ہیں، لیکن اسباب اور علاج کے طریقے مختلف ہیں۔

کوڑا کرکٹ جمع کرنے والا کیسے کام کرتا ہے اور لیک کیوں ہوتے ہیں؟

ART (Android Runtime) ہم آہنگ کمپیکشن کے ساتھ نسلی کوڑا کرکٹ جمع کرنے کا استعمال کرتا ہے۔ میموری کو نوجوان نسل (Young)، پرانی نسل (Old) اور بڑی اشیاء (Large) میں تقسیم کیا گیا ہے۔ متعدد GC سائیکل سے بچنے والی اشیاء Old نسل میں منتقل ہو جاتی ہیں، جہاں جمع کرنا کم بار بار ہوتا ہے — اس سے عام سائیکل تیز ہو جاتے ہیں۔

GC اس وقت شروع ہوتا ہے جب heap ایک خاص قبضے کی حد (عام طور پر 75-85%) تک پہنچ جاتا ہے۔ GC کے دوران، ایپلیکیشن کے تمام تھریڈ روک دیے جاتے ہیں (STW — Stop The World)۔ جتنی زیادہ زندہ اشیاء، اتنا ہی طویل وقفہ۔ لیک زندہ اشیاء کی تعداد بڑھاتے ہیں، GC کے وقفوں کو لمبا کرتے ہیں۔

جمع کرنے والا GC Roots سے گراف کو گھوم کر زندہ اشیاء کا تعین کرتا ہے: جامد فیلڈز، فعال تھریڈ کے اسٹیک متغیرات، JNI حوالہ جات۔ ان جڑوں سے حوالوں کے ذریعے قابل رسائی کوئی بھی آبجیکٹ زندہ سمجھا جاتا ہے — چاہے ڈویلپر جانتا ہو کہ اس کی مزید ضرورت نہیں ہے۔

kotlin
// مثال: GC Root کے طور پر جامد مجموعہ — مستقل لیک
object GlobalHolder {
    val listeners = mutableListOf<WeakReference<Any>>()
}

class LeakingFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle ?= null) {
        super.onCreate(savedInstanceState)
        GlobalHolder.listeners.add(WeakReference(this))
        // WeakReference GC کو نہیں روکتا — صحیح رویہ
    }
}

WeakReference مسئلہ حل کرتا ہے: GC زندہ اشیاء کا تعین کرتے وقت کمزور حوالوں کو نظر انداز کرتا ہے۔ اگر کسی آبجیکٹ پر صرف کمزور حوالے باقی ہیں، تو یہ قریب ترین GC سائیکل میں جمع کر لیا جائے گا۔

Android اور iOS میں عام لیک کے منظرنامے

Activity Context — Android میں سب سے وسیع لیک منظرنامہ۔ اگر سنگلٹن، جامد فیلڈ یا طویل المدت سروس Activity Context کا حوالہ محفوظ کرتی ہے، تو تمام Views سمیت پوری Activity GC کے ذریعے جمع نہیں کی جا سکتی۔ حل: طویل المدت اشیاء کے لیے Application Context استعمال کریں۔

Handler اور بھیجے گئے پیغامات — Handler.postDelayed(runnable, delay) Main Looper قطار میں ایک پیغام ڈالتا ہے۔ اگر تاخیر ختم ہونے سے پہلے Activity تباہ ہو جاتی ہے، تو پیغام اب بھی قطار میں ہے اور Runnable → گمنام کلاس → بیرونی کلاس (Activity) کے ذریعے حوالہ رکھتا ہے۔

kotlin
class SafeActivity : AppCompatActivity() {
    private val mainHandler = Handler(Looper.getMainLooper())
    private val callback = Runnable { /* update UI */ }

    override fun onResume() {
        super.onResume()
        mainHandler.postDelayed(callback, 5000)
    }

    override fun onPause() {
        mainHandler.removeCallbacks(callback) // لازمی: قطار صاف کریں
        super.onPause()
    }
}

اندرونی کلاسیں — ایک غیر جامد اندرونی کلاس کا بیرونی کلاس کے نمونے پر ایک مضمر حوالہ ہوتا ہے۔ اگر بیرونی کلاس Activity ہے اور اندرونی کلاس باہر کہیں منتقل کی جاتی ہے (مثال کے طور پر، RecyclerView.Adapter میں)، تو Activity جمع نہیں کی جا سکتی۔

  • TimerTask اور ScheduledExecutorService — Activity تباہی سے پہلے طے شدہ کام
  • BroadcastReceiver — onPause/onDestroy میں رجسٹریشن منسوخ نہ کرنے پر Context رکھتا رہتا ہے
  • View حوالہ کے ساتھ ViewModel — ViewModel Activity سے زیادہ زندہ رہتا ہے، View کا حوالہ لیک کا سبب بنتا ہے
  • Retrofit Call — اگر Call منسوخ نہ کیا جائے، تو جواب تباہ شدہ Fragment پر آتا ہے

میموری لیک تشخیصی ٹولز

Android Studio Memory Profiler — ریئل ٹائم heap نگرانی کے لیے بلٹ ان ٹول۔ استعمال شدہ میموری کا گراف، مختصات کی تعداد اور قسم کے لحاظ سے اشیاء دکھاتا ہے۔ MAT میں تجزیہ کے لیے heap dump ریکارڈ کرنے اور HPROF فارمیٹ میں برآمد کرنے کی اجازت دیتا ہے۔

Eclipse MAT (Memory Analyzer Tool) — ڈیسک ٹاپ heap dump تجزیہ کار۔ خود بخود Leak Suspects رپورٹس بناتا ہے، جو سب سے بڑے retained size والی اشیاء کو نمایاں کرتا ہے اور ہر مشکوک آبجیکٹ کے لیے ممکنہ GC root chain تجویز کرتا ہے۔

Xcode Memory Graph Debugger — iOS کے لیے۔ ایپلیکیشن روکتا ہے اور آبجیکٹ گراف کو بصری بناتا ہے۔ Retain cycles کو سرخ رنگ میں نمایاں کیا جاتا ہے؛ کسی بھی آبجیکٹ پر کلک کر کے اس کا retain count اور حوالے دیکھ سکتے ہیں۔

ٹولصلاحیتیںپیچیدگی
Memory Profilerریئل ٹائم گراف، heap dump، Object Allocation Trackingکم
Eclipse MATDominator tree، Leak Suspects، OQL سوالاتدرمیانی
LeakCanaryخودکار پتہ لگانا، نوٹیفکیشن میں لیک کا نشانکم سے کم
Xcode Memory Graphretain cycles کا بصری گراف، زندہ اشیاء کی فہرستکم

Uber Engineering Blog کے مطابق، CI/CD پائپ لائن میں خودکار میموری پروفائلنگ (LeakCanary + heap dump تجزیہ) کو ضم کرنے سے 3 ماہ کے اندر پروڈکشن میں میموری سے متعلق واقعات میں 60% کمی آتی ہے۔

لیک ختم کرنے کے طریقے

Context تبدیل کریں — اگر کوئی آبجیکٹ Activity سے زیادہ زندہ رہتا ہے، تو applicationContext استعمال کریں۔ تمام طویل المدت اشیاء (سنگلٹن، ریپوزیٹری، ڈیٹا بیس ہیلپر) کو Activity Context نہیں بلکہ Application Context ملنا چاہیے۔ مستثنیٰ: UI اجزاء جنہیں Activity کے مخصوص تھیم یا وسائل تک رسائی کی ضرورت ہے۔

Lifecycle-aware اجزاء — LifecycleObserver، DefaultLifecycleObserver یا ری ایکٹو ایکسٹینشنز کا استعمال onDestroy پر خود بخود سبسکرپشنز منسوخ کرتا ہے۔ Android Jetpack lifecycleScope اور viewModelScope فراہم کرتا ہے، جو متعلقہ لائف سائیکل ایونٹ کے ذریعے صاف کیے جاتے ہیں۔

جامد اندرونی کلاس — اگر اندرونی کلاس کو بیرونی کلاس کے فیلڈز تک رسائی کی ضرورت نہیں ہے، تو اسے static بنائیں۔ جامد اندرونی کلاس کا بیرونی کلاس پر کوئی مضمر حوالہ نہیں ہوتا۔ اگر رسائی کی ضرورت ہو، تو واضح حوالہ کے لیے WeakReference استعمال کریں۔

kotlin
class MyActivity : AppCompatActivity() {

    // ❌ غیر جامد اندرونی کلاس — MyActivity کا مضمر حوالہ
    inner class BadListener : SomeListener {
        override fun onEvent() { /*...*/ }
    }

    // ✅ جامد اندرونی کلاس — کوئی مضمر حوالہ نہیں
    class GoodListener(private val activityRef: WeakReference<MyActivity>) : SomeListener {
        override fun onEvent() { /*...*/ }
    }
}

iOS میں، کیپچر فہرستیں استعمال کریں: closure میں [weak self] جو اپنے بنانے والے سے زیادہ زندہ رہ سکتے ہیں۔ مندوبین کے لیے، کمزور حوالے استعمال کریں (weak var delegate)۔ ان closure کے لیے جن کے صرف self کی زندگی کے دوران بلائے جانے کی ضمانت ہے، [unowned self] استعمال کیا جا سکتا ہے، لیکن احتیاط سے — ختم شدہ آبجیکٹ تک رسائی کریش کا سبب بنے گی۔

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

خاص ٹولز کے بغیر لیک کیسے تلاش کریں؟

Android میں، کئی اسکرین ٹرانزیشن کریں (Activity A → B → A → B) اور adb shell dumpsys meminfo package_name چیک کریں۔ اگر Total PSS مستقل طور پر بڑھتا ہے اور اصل قدر پر واپس نہیں آتا — تو لیک ہے۔ iOS میں، اسی طرح: بصری معائنہ کے لیے Xcode میں Debug Memory Graph استعمال کریں۔

کیا Kotlin coroutine لیک کا سبب بن سکتا ہے؟

ہاں، اگر جزو تباہ ہونے پر CoroutineScope منسوخ نہ کیا جائے۔ GlobalScope میں شروع کیا گیا coroutine Activity کے finish() کے بعد بھی عمل میں رہتا ہے۔ حل: viewModelScope (onCleared میں منسوخ) یا lifecycleScope (onDestroy میں منسوخ) استعمال کریں۔ حسب ضرورت دائرہ کار کے لیے، LifecycleOwner کے ذریعے lifecycle-aware دائرہ کار بنائیں۔

Bitmap لیک کو کیسے متاثر کرتا ہے؟

Bitmap پکسل ڈیٹا Java heap کے بجائے مقامی heap (native heap) میں محفوظ کرتا ہے۔ اس کا مطلب ہے کہ Java GC Bitmap کا اصل سائز نہیں دیکھتا۔ اگر Bitmap پر recycle() نہ بلایا جائے یا حوالہ null نہ کیا جائے، تو مقامی میموری آزاد نہیں ہوگی۔ چھوٹی کاپیاں لوڈ کرنے کے لیے BitmapFactory کو inSampleSize کے ساتھ اور خودکار کیش مینجمنٹ کے لیے Glide/Coil استعمال کریں۔

جامد فیلڈ کے ذریعے لیک کیا ہے؟

جامد فیلڈ ایک GC Root ہے۔ یہ اس وقت تک زندہ رہتا ہے جب تک کلاس لوڈ ہے (Android میں — جب تک Process زندہ ہے)۔ اگر جامد فیلڈ Activity، Bitmap، View یا کسی بھی دوسری بھاری آبجیکٹ کا حوالہ دیتا ہے، تو وہ آبجیکٹ کبھی GC کے ذریعے جمع نہیں ہوگی۔ جامد فیلڈ ایک ابدی حوالہ ہے۔ حل: صرف WeakReference محفوظ کریں یا onDestroy میں جامد فیلڈ کو null کریں۔

ARC کے ساتھ iOS میں لیک سے کیسے بچیں؟

ARC خود بخود اشیاء کو آزاد کرتا ہے جب مضبوط حوالہ شمار صفر ہو جاتا ہے۔ ARC کے تحت لیک کا واحد راستہ retain cycle ہے۔ والدین→بچہ حوالوں کے لیے ہمیشہ weak استعمال کریں جہاں بچہ والدین سے زیادہ زندہ رہ سکتا ہے (مندوبین، ڈیٹا سورس)۔ closure کے لیے، کیپچر فہرست [weak self] استعمال کریں اور closure کے اندر self کو nil کے لیے چیک کریں۔

خلاصہ

  • میموری لیک — آبجیکٹ کوڈ کے لیے ناقابل رسائی ہے لیکن GC سے ہٹایا نہیں گیا کیونکہ GC Root سے ایک فعال حوالہ موجود ہے
  • GC Roots میں جامد فیلڈز، اسٹیک متغیرات اور JNI حوالہ جات شامل ہیں؛ ان سے قابل رسائی کوئی بھی آبجیکٹ زندہ ہے
  • Context لیک — Android میں سب سے وسیع مسئلہ: سنگلٹن یا جامد فیلڈ میں Activity Context منتقل کرنا
  • Handler اور اندرونی کلاس — دوسری سب سے عام وجہ: Looper قطار میں منسوخ نہ شدہ پیغامات Activity کا حوالہ رکھتے ہیں
  • LeakCanary — معیاری خودکار پتہ لگانے کا آلہ؛ heap dump لیتا ہے اور صحیح GC root chain دکھاتا ہے
  • lifecycleScope اور viewModelScope coroutine کے ذریعے لیک کا مسئلہ حل کرتے ہیں — تباہی پر خودکار منسوخی
  • CI/CD میں میموری پروفائل کریں: ڈیبگ میں LeakCanary + ٹیسٹ رن میں heap dump تجزیہ کو نئے لیک پر انضمام روکنا چاہیے

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

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

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

مزید پڑھیں