मेमोरी लीक: यह क्या है, सामान्य परिदृश्य और निदान

लेखक: IT Sectr प्रकाशित: 2026-07-29 पढ़ने का समय: 10 मिनट

मेमोरी लीक (memory leak) — वह स्थिति जब एप्लिकेशन उन ऑब्जेक्ट्स द्वारा घेरी गई मेमोरी को मुक्त नहीं करता जिनकी अब आवश्यकता नहीं है। मोबाइल डेवलपमेंट में यह विशेष रूप से महत्वपूर्ण है: सीमित heap और swap की अनुपस्थिति OutOfMemoryError और एप्लिकेशन क्रैश का कारण बनती है। Purdue University (2022) के अनुसार, Google Play में 35% Android एप्लिकेशन में कम से कम एक मेमोरी लीक होता है। आइए सामान्य परिदृश्यों, निदान उपकरणों और समाधान विधियों पर चर्चा करें।

मुख्य बिंदु

  • GC Root — प्रवेश बिंदु जिसके माध्यम से कचरा संग्रहकर्ता जीवित ऑब्जेक्ट्स निर्धारित करता है
  • Context लीक — सिंगलटन में Activity Context पास करने से पूरी View hierarchy रुक जाती है
  • 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 generation में चले जाते हैं, जहाँ संग्रह कम बार होता है — इससे सामान्य चक्र तेज होते हैं।

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 का उपयोग करें। सभी लंबे समय तक चलने वाले ऑब्जेक्ट्स (सिंगलटन, रिपॉजिटरी, डेटाबेस हेल्पर्स) को Application Context प्राप्त करना चाहिए, Activity 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 में, कैप्चर सूचियों का उपयोग करें: क्लोज़र में [weak self] जो अपने निर्माता से अधिक जीवित रह सकते हैं। डेलिगेट के लिए, कमजोर संदर्भों का उपयोग करें (weak var delegate)। उन क्लोज़र के लिए जिनके केवल self के जीवनकाल के दौरान ही कॉल किए जाने की गारंटी है, [unowned self] का उपयोग किया जा सकता है, लेकिन सावधानी से — डीलोकेटेड ऑब्जेक्ट तक पहुँचने से क्रैश होगा।

अक्सर पूछे जाने वाले प्रश्न

बिना विशेष उपकरणों के लीक कैसे खोजें?

Android में, कई स्क्रीन परिवर्तन (Activity A → B → A → B) करें और adb shell dumpsys meminfo package_name जाँचें। यदि Total PSS लगातार बढ़ता है और मूल मान पर वापस नहीं आता — तो लीक है। iOS में, समान रूप से: दृश्य निरीक्षण के लिए Xcode में Debug Memory Graph का उपयोग करें।

क्या Kotlin कोरूटीन लीक का कारण बन सकता है?

हाँ, यदि घटक नष्ट होने पर CoroutineScope रद्द नहीं किया गया। GlobalScope में लॉन्च किया गया कोरूटीन Activity के finish() के बाद भी निष्पादित होता रहता है। समाधान: viewModelScope (onCleared में रद्द) या lifecycleScope (onDestroy में रद्द) का उपयोग करें। कस्टम स्कोप के लिए, LifecycleOwner के माध्यम से lifecycle-aware स्कोप बनाएँ।

Bitmap लीक को कैसे प्रभावित करता है?

Bitmap पिक्सेल डेटा को Java heap के बजाय native heap में संग्रहीत करता है। इसका मतलब है कि Java GC Bitmap का वास्तविक आकार नहीं देखता। यदि Bitmap पर recycle() कॉल नहीं किया जाता या संदर्भ शून्य नहीं किया जाता, तो native मेमोरी मुक्त नहीं होती। छोटी प्रतियां लोड करने के लिए BitmapFactory का inSampleSize के साथ और स्वचालित कैश प्रबंधन के लिए Glide/Coil का उपयोग करें।

स्थैतिक फ़ील्ड के माध्यम से लीक क्या है?

स्थैतिक फ़ील्ड — एक GC Root है। यह तब तक जीवित रहता है जब तक क्लास लोड है (Android में — जब तक Process जीवित है)। यदि स्थैतिक फ़ील्ड Activity, Bitmap, View या किसी अन्य भारी ऑब्जेक्ट को संदर्भित करता है, तो वह ऑब्जेक्ट कभी GC द्वारा एकत्र नहीं किया जाएगा। स्थैतिक फ़ील्ड एक शाश्वत संदर्भ है। समाधान: केवल WeakReference संग्रहीत करें या onDestroy में स्थैतिक फ़ील्ड को शून्य करें।

ARC के साथ iOS में लीक से कैसे बचें?

ARC स्वचालित रूप से ऑब्जेक्ट्स को मुक्त करता है जब मजबूत संदर्भ गणना शून्य हो जाती है। ARC के तहत लीक का एकमात्र तरीका retain cycle है। हमेशा parent→child संदर्भों के लिए weak का उपयोग करें जहाँ child parent से अधिक जीवित रह सकता है (डेलिगेट, data source)। क्लोज़र के लिए, कैप्चर सूची [weak self] का उपयोग करें और क्लोज़र के अंदर self को nil के लिए जाँचें।

सारांश

  • मेमोरी लीक — ऑब्जेक्ट कोड के लिए अप्राप्य है लेकिन GC द्वारा हटाया नहीं गया क्योंकि GC Root से एक सक्रिय संदर्भ मौजूद है
  • GC Roots में स्थैतिक फ़ील्ड, स्टैक वेरिएबल और JNI संदर्भ शामिल हैं; उनसे पहुँचा जा सकने वाला कोई भी ऑब्जेक्ट जीवित है
  • Context लीक — Android में सबसे व्यापक समस्या: सिंगलटन या स्थैतिक फ़ील्ड में Activity Context पास करना
  • Handler और आंतरिक वर्ग — दूसरा सबसे सामान्य कारण: Looper कतार में रद्द न किए गए संदेश Activity का संदर्भ रखते हैं
  • LeakCanary — मानक स्वचालित पहचान उपकरण; heap dump लेता है और सटीक GC root chain दिखाता है
  • lifecycleScope और viewModelScope कोरूटीन के माध्यम से लीक की समस्या हल करते हैं — नष्ट होने पर स्वचालित रद्दीकरण
  • CI/CD में मेमोरी प्रोफाइल करें: डिबग में LeakCanary + परीक्षण रन में heap dump विश्लेषण नए लीक पर मर्ज को ब्लॉक करना चाहिए

हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे

IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।

परियोजना पर चर्चा करें

यह भी पढ़ें