میموری لیک — موبائل ڈویلپمنٹ میں سب سے مشکل مسائل میں سے ایک ہے۔ ایپ کا میموری استعمال مسلسل بڑھتا رہتا ہے جب تک کہ یہ OS کے مقرر کردہ حد تک نہ پہنچ جائے، جس کے بعد OutOfMemoryError یا جبری خاتمہ ہوتا ہے۔ Square Engineering کے مطابق، تقریباً 40% Android ایپس میں کم از کم ایک میموری لیک ہوتی ہے جو صرف پروفائلنگ کے ذریعے دریافت کی جا سکتی ہے۔ آئیے اسباب اور میموری بڑھنے سے روکنے کے طریقوں کا جائزہ لیتے ہیں۔
اہم نکات
میموری لیک — ایک صورت حال جہاں ایک آبجیکٹ جو اب ایپ کے لیے ضروری نہیں ہے، ہیپ میں برقرار رہتا ہے کیونکہ GC روٹ سیٹ سے ایک فعال حوالہ اب بھی اس کی طرف اشارہ کر رہا ہے۔ کوڑا کرکٹ جمع کرنے والا ایسے آبجیکٹ کو زندہ سمجھتا ہے اور اسے ہٹاتا نہیں۔
میموری پھولنا — ایک وسیع تر مسئلہ جہاں ایپ اپنے موجودہ کاموں کو انجام دینے کے لیے ضرورت سے زیادہ میموری استعمال کرتی ہے۔ اسباب: ضرورت سے زیادہ کیشنگ، آبجیکٹ کی نقل، غیر موزوں ڈیٹا ڈھانچے، اور ہیپ کی بکھراوٹ۔
Android میں، ہر ایپ کو ایک محدود ہیپ مختص کیا جاتا ہے (عام طور پر ڈیوائس اور OS ورژن کے لحاظ سے 64–512 MB)۔ iOS میں، حد کم سخت ہے، لیکن حد کے قریب پہنچنے پر سسٹم میموری وارننگ بھیجتا ہے۔
| خصوصیت | Android | iOS |
|---|---|---|
| ہیپ کی حد | 64–512 MB (ڈیوائس پر منحصر) | ضمنی (سسٹم) |
| کوڑا کرکٹ جمع کرنا | ART (بیک وقت، کمپیکٹ) | ARC (خودکار حوالہ گنتی) |
| لیک کا طریقہ کار | GC روٹ حوالہ جات | برقرار رکھنے کے چکر (مضبوط حوالہ چکر) |
| نتیجہ | OutOfMemoryError | میموری وارننگ → خاتمہ |
Facebook Engineering Blog کے مطابق، میموری لیک موبائل ایپس میں تقریباً ~15% کریش رپورٹس کا سبب بنتی ہیں۔ Android میں، میموری کم ہونے پر بار بار GC رکنے کی وجہ سے ANR بھی شامل ہو جاتے ہیں۔
Activity کا جامد حوالہ — ایک کلاسک Android لیک۔ اگر کوئی جامد فیلڈ یا سنگلٹن Activity کا حوالہ رکھتا ہے، تو singleton زندہ رہنے تک finish() کے بعد بھی GC اسے جمع نہیں کرے گا۔ Activity ایک بھاری آبجیکٹ ہے جس میں ویو کا درجہ بندی، وسائل اور Context شامل ہوتے ہیں۔
object LeakHolder {
var activityRef: Activity ?= null // leak: static reference to Activity
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle ?= null) {
super.onCreate(savedInstanceState)
LeakHolder.activityRef = this // ❌ MainActivity will never be GC'd
}
}
گمنام کلاسز اور لیمبڈا — بیرونی کلاس کا ایک حوالہ واضح طور پر رکھتے ہیں۔ اگر Runnable یا Callback کسی بیرونی سروس کو بھیجا جائے اور Activity تباہ ہو جائے، تو گمنام کلاس کا آبجیکٹ ابھی بھی قطار میں رہتا ہے اور Activity کو کوڑا کرکٹ میں جمع ہونے سے روکتا ہے۔
iOS میں اصل مسئلہ برقرار رکھنے کے چکر ہیں: دو آبجیکٹ ایک دوسرے کے مضبوط حوالہ جات رکھتے ہیں اور ARC کسی کے لیے بھی حوالہ شمار صفر نہیں کر سکتا۔ عام مثال: ایک closure جو self کو مضبوطی سے پکڑتا ہے، اور self جو closure کا حوالہ رکھتا ہے۔
LeakCanary — Android میں خودکار لیک کھوج کے لیے Square کی لائبریری۔ Activity یا Fragment تباہ ہونے کے بعد، یہ چیک کرتی ہے کہ آبجیکٹ GC نے جمع کیا یا نہیں۔ اگر نہیں، تو یہ ہیپ ڈمپ لیتی ہے اور لیک ٹریس دکھاتی ہے۔
// LeakCanary 2.x — auto-integration via Application
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// LeakCanary auto-installs in debug build
// via ContentProvider — zero code setup
}
}
// Force check invocation
AppWatcher.objectWatcher.watch(watchedObject, "leak description")
Android Studio Profiler — ریئل ٹائم میموری مانیٹرنگ کے لیے بلٹ ان ٹول۔ یہ ہیپ ڈمپ ریکارڈ کرنے، مشکوک آبجیکٹ (Retained Size > 1 MB) تلاش کرنے اور ہر آبجیکٹ تک GC روٹ پاتھ ٹریس کرنے کی اجازت دیتا ہے۔
iOS کے لیے Xcode Memory Graph Debugger استعمال کریں۔ یہ میموری میں آبجیکٹ گراف کو دیکھتا ہے، برقرار رکھنے کے چکر دکھاتا ہے اور فوری طور پر سرکلر حوالوں کا پتہ لگانے کی اجازت دیتا ہے۔ طویل مدتی مانیٹرنگ کے لیے Instruments > Allocations بھی دستیاب ہے۔
WeakReference — ان حوالوں کے لیے بنیادی طریقہ کار جو کوڑا کرکٹ جمع کرنے میں رکاوٹ نہیں بننے چاہئیں۔ اگر GC کسی آبجیکٹ کو جمع کرنے کا فیصلہ کرتا ہے، تو WeakReference null لوٹاتا ہے۔ یہ کال بیکس، سننے والوں اور بیک گراؤنڈ تھریڈز سے UI اجزاء کے حوالوں کے لیے استعمال ہوتا ہے۔
Lifecycle-aware اجزاء — Android Jetpack (Lifecycle, LiveData, Flow, coroutines) میں نافذ کردہ ایک آرکیٹیکچرل نقطہ نظر۔ onDestroy پر سبسکرپشنز خود بخود منسوخ ہو جاتی ہیں، جو لیک کی مرکزی کلاس کو ختم کر دیتی ہیں۔
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)
// coroutine auto-cancels on onCleared()
}
}
}
viewModelScope اور lifecycleScope — Android میں بلٹ ان CoroutineScope جو متعلقہ لائف سائیکل ایونٹ پر منسوخ ہو جاتے ہیں۔ یہ coroutines کے ذریعے لیک کو ختم کرتا ہے — جدید Android ڈویلپمنٹ میں سب سے عام منظرنامہ۔
Android Studio میں Memory Profiler — ہیپ مانیٹرنگ کے لیے بنیادی آلہ۔ یہ لائیو ایلوکیشن، ہیپ سنیپ شاٹس اور قسم کے لحاظ سے آبجیکٹ کی گنتی دکھاتا ہے۔ یہ ڈمپ ریکارڈ کرنے اور مشکوک آبجیکٹ تلاش کرنے کے لیے MAT (Memory Analyzer Tool) میں تجزیہ کرنے کی اجازت دیتا ہے۔
Eclipse MAT — ڈیسک ٹاپ ہیپ ڈمپ تجزیہ کار۔ Android Studio سے HPROF فائل لوڈ کرنے کے بعد، MAT ایک ڈومینیٹر ٹری بناتا ہے، ہر آبجیکٹ کا retain size دکھاتا ہے اور Leak Suspects Report کے ذریعے خودکار مشکوک لیک تجزیہ پیش کرتا ہے۔
Xcode Memory Graph — برقرار رکھنے کے چکروں کا بصری ڈیبگر۔ Memory Graph Debugger بٹن پر کلک کرنے پر، Xcode ایپ کو روکتا ہے، میموری میں ایک مکمل آبجیکٹ گراف بناتا ہے اور برقرار رکھنے کے چکروں کو سرخ رنگ میں نمایاں کرتا ہے۔
| آلہ | پلیٹ فارم | خصوصیت |
|---|---|---|
| LeakCanary | Android | تباہی کے بعد خودکار لیک کھوج |
| Memory Profiler | Android Studio | ہیپ ڈمپ + لائیو ایلوکیشن |
| Eclipse MAT | Android | ڈومینیٹر ٹری، Leak Suspects Report |
| Memory Graph | iOS (Xcode) | برقرار رکھنے کے چکروں کا بصری آلہ |
Google I/O 2023 کے مطابق، ڈیبگ بلڈ میں LeakCanary استعمال کرنے والی ایپس اپنانے کے پہلے 2 مہینوں میں میموری سے متعلق کریشز کو 30–50% کم کرتی ہیں۔ پروجیکٹ آن بورڈنگ مرحلے میں LeakCanary شامل کرنے کی سفارش کی جاتی ہے۔
اکثر پوچھے گئے سوالات
لیک — آبجیکٹ جو کوڈ کے لیے قابل رسائی نہیں لیکن فعال حوالوں کی وجہ سے GC جمع نہیں کرتا۔ پھولنا — ایپ ان آبجیکٹ کو رکھتی ہے جو منطقی طور پر ضروری ہیں لیکن ضرورت سے زیادہ مقدار میں (مثال: 80 MB چلنے والی ایپ میں 50 MB کیش)۔ پھولنا آرکیٹیکچرل طور پر حل ہوتا ہے، لیک — درست حوالہ جات کے انتظام سے۔
LeakCanary ObjectWatcher استعمال کرتا ہے — Activity کے onDestroy() کے بعد، یہ Activity پر ایک WeakReference بناتا ہے اور GC چلاتا ہے۔ اگر 5 سیکنڈ کے بعد WeakReference صاف نہ ہو، تو LeakCanary ہیپ ڈمپ لیتا ہے، GC Root سے آبجیکٹ تک سب سے چھوٹی حوالہ زنجیر کا تجزیہ کرتا ہے اور فائل اور کوڈ لائن کے ساتھ صحیح لیک اسٹیک دکھاتا ہے۔
Bitmap جاوا ہیپ سے باہر مقامی میموری (native heap) میں جگہ لیتا ہے۔ ایک Bitmap کا سائز = چوڑائی × اونچائی × 4 بائٹ (ARGB_8888)۔ ایک 12 MP تصویر (4000×3000) 48 MB لیتی ہے۔ Android ہمیشہ بروقت مقامی میموری خالی نہیں کر سکتا، لہٰذا کئی Bitmaps جمع ہونے سے کافی جاوا ہیپ ہونے پر بھی OOM ہوتا ہے۔
برقرار رکھنے کا چکر — ARC میں ایک صورت حال جہاں دو آبجیکٹ ایک دوسرے کے مضبوط حوالہ جات رکھتے ہیں اور حوالہ شمار کبھی صفر تک نہیں پہنچتا۔ عام مثال: ایک ViewController جس میں closure کا مضبوط حوالہ ہے، اور closure self کو مضبوطی سے پکڑتا ہے۔ حل: closures میں [weak self] یا [unowned self] استعمال کریں۔
ہیپ کا سائز ڈیوائس اور Android ورژن پر منحصر ہے۔ پرانے ڈیوائسز (API 15–24) کے لیے — 64–128 MB۔ جدید (API 25+) کے لیے — 256–512 MB۔ صحیح قیمت ActivityManager.getMemoryClass() کے ذریعے حاصل کی جا سکتی ہے۔ بڑی ایپس (گیمز، ایڈیٹرز) کے لیے، manifest میں largeHeap=true 1 GB تک فراہم کرتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں