LeakCanary Square द्वारा Android एप्लिकेशन में स्वचालित मेमोरी लीक डिटेक्शन के लिए एक ओपन-सोर्स लाइब्रेरी है। यह डेवलपमेंट प्रक्रिया में एकीकृत होती है और Activity, Fragment, ViewModel और अन्य घटकों के जीवनचक्र पर रियल टाइम में नज़र रखती है, लीक होते ही संकेत देती है। Square Open Source के अनुसार, लाइब्रेरी हज़ारों प्रोजेक्ट्स में उपयोग की जाती है और Android पर मेमोरी डायग्नोस्टिक्स के लिए डी-फैक्टो मानक मानी जाती है।
मुख्य बिंदु
LeakCanary Android एप्लिकेशन में स्वचालित मेमोरी लीक डिटेक्शन के लिए Square द्वारा विकसित एक लाइब्रेरी है। यह एप्लिकेशन बिल्ड प्रक्रिया में एकीकृत होती है और स्वचालित रूप से निगरानी करती है कि जिन ऑब्जेक्ट्स को नष्ट हो जाना चाहिए (Activity, Fragment, View) वे मेमोरी में रहते हैं या नहीं। लीक का पता चलने पर, LeakCanary हीप डंप जनरेट करता है और ऑब्जेक्ट को धारण करने वाली रेफरेंस चेन का विश्लेषण करता है।
लाइब्रेरी Android समुदाय में एक मानक बन गई है: GitHub के अनुसार, प्रोजेक्ट के 28 हज़ार से अधिक स्टार हैं और इसका उपयोग Google, Uber, Airbnb और Facebook के एप्लिकेशन में किया जाता है। LeakCanary दो मुख्य संस्करणों में उपलब्ध है: क्लासिक 1.x (मैन्युअल कॉन्फ़िगरेशन के साथ) और आधुनिक 2.x (ContentProvider के माध्यम से स्वचालित एकीकरण)। संस्करण 2.x में Application क्लास को संशोधित करने की आवश्यकता नहीं — पूर्ण कार्यक्षमता के लिए डिपेंडेंसी ही पर्याप्त है।
LeakCanary का मुख्य कार्य यह पता लगाना है कि जब किसी ऑब्जेक्ट का जीवनचक्र समाप्त हो जाता है तब भी वह मेमोरी में मौजूद रहता है। यह स्टैटिक फ़ील्ड्स, सिंगलटन, अपंजीकृत कॉलबैक, अनाम क्लासेस और बाहरी ऑब्जेक्ट्स को कैप्चर करने वाले क्लोज़र के माध्यम से लीक के लिए विशिष्ट है।
मोबाइल उपकरणों पर सीमित RAM के कारण Android पर मेमोरी लीक डेस्कटॉप की तुलना में अधिक गंभीर हैं। प्रत्येक स्क्रीन ट्रांज़िशन पर 5–10 MB का लीक भी 30–40 मिनट के एप्लिकेशन उपयोग के बाद OutOfMemoryError का कारण बन सकता है। LeakCanary ऐसी समस्याओं का पता डेवलपमेंट चरण में ही लगाता है, प्रोडक्शन में क्रैश होने का इंतज़ार किए बिना।
LeakCanary फोर्स्ड गार्बेज कलेक्शन के साथ कमज़ोर रेफरेंस (WeakReference) का उपयोग करता है। जब कोई Activity या Fragment onDestroy कॉल करता है, LeakCanary उस ऑब्जेक्ट पर WeakReference बनाता है और थोड़ी देरी (डिफ़ॉल्ट रूप से 5 सेकंड) के बाद GC ट्रिगर करता है। यदि GC के बाद ऑब्जेक्ट WeakReference के माध्यम से अभी भी सुलभ है, तो यह एक मज़बूत रेफरेंस द्वारा धारित है — एक लीक रिकॉर्ड की जाती है।
लीक का पता लगने के बाद, LeakCanary एक हीप डंप (मेमोरी डंप) बनाता है — HPROF प्रारूप में एप्लिकेशन मेमोरी का पूर्ण स्नैपशॉट। इसके बाद अंतर्निहित विश्लेषक (संस्करण 2.x के लिए Shark) GC Roots से लीक हुए ऑब्जेक्ट तक पहुँच ग्राफ बनाता है और सबसे छोटा रास्ता ढूँढता है — ऑब्जेक्ट को मेमोरी में धारण करने वाली रेफरेंस चेन।
// LeakCanary डिटेक्शन का सरलीकृत तर्क
class ObjectWatcher {
private val watchedReferences = CopyOnWriteArrayList<KeyedWeakReference>()
fun watch(watchedObject: Any, description: String) {
val reference = KeyedWeakReference(watchedObject, description)
watchedReferences.add(reference)
BackgroundHandler.postDelayed({
checkForLeaks()
}, 5000)
}
private fun checkForLeaks() {
GcTrigger.runGc() // फोर्स्ड GC
for (ref in watchedReferences) {
if (ref.get() != null) {
onLeakFound(ref) // ऑब्जेक्ट GC से बच गया — यह लीक है
}
}
}
}
मुख्य बिंदु GcTrigger.runGc() का फोर्स्ड कॉल है। इसके बिना, वास्तव में लीक हुए ऑब्जेक्ट और उस ऑब्जेक्ट के बीच अंतर करना असंभव है जिसे GC ने अभी तक एकत्र नहीं किया है। LeakCanary इसे तीन बार तक करता है: यदि तीन GC चक्रों के बाद भी ऑब्जेक्ट मेमोरी में है, तो लीक की पुष्टि हो जाती है।
Shark LeakCanary 2.x में निर्मित हीप डंप विश्लेषक है, जो Kotlin में लिखा गया है। पिछले HAHA विश्लेषक के विपरीत, Shark पूरी HPROF फ़ाइल को मेमोरी में लोड नहीं करता, बल्कि न्यूनतम आवंटन के साथ इसके ऑब्जेक्ट ग्राफ को ट्रैवर्स करता है। यह विश्लेषण के दौरान RAM खपत को 50 MB से घटाकर 2–5 MB करता है और विश्लेषण समय को 30 सेकंड से घटाकर 1–3 सेकंड करता है।
आधुनिक Android प्रोजेक्ट में LeakCanary 2.x इंस्टॉल करने में build.gradle में एक लाइन लगती है। लाइब्रेरी स्वचालित इनिशियलाइज़ेशन के लिए ContentProvider का उपयोग करती है — Application क्लास को संशोधित करने या MainActivity में कोई कोड जोड़ने की आवश्यकता नहीं। डिपेंडेंसी केवल debug बिल्ड के लिए जोड़ी जाती है ताकि रिलीज़ APK में अतिरिक्त कोड न हो।
// build.gradle (app/module)
dependencies {
// debugImplementation — लाइब्रेरी केवल debug बिल्ड के लिए
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}
डिपेंडेंसी जोड़ने और प्रोजेक्ट को रीबिल्ड करने के बाद, LeakCanary स्वचालित रूप से एप्लिकेशन में दिखाई देता है। पहले लॉन्च पर, लाइब्रेरी सक्रियण की पुष्टि करने वाला एक सिस्टम नोटिफिकेशन दिखाती है। सभी पाई गई लीक नोटिफिकेशन के रूप में दिखाई देती हैं — नोटिफिकेशन पर टैप करने से विस्तृत रिपोर्ट (LeakTrace) वाली स्क्रीन खुलती है।
अनुकूलन के लिए, आप अपना स्वयं का AppWatcherInstaller बना सकते हैं और पैरामीटर ओवरराइड कर सकते हैं: GC टाइमआउट, ट्रैक किए जाने वाले ऑब्जेक्ट प्रकारों की सूची, डिस्क पर हीप डंप सेविंग सक्षम करना। हालांकि, 90% प्रोजेक्ट्स के लिए डिफ़ॉल्ट कॉन्फ़िगरेशन इष्टतम है।
संस्करण 2.12 से शुरू, LeakCanary ViewModel, कोरूटीन स्कोप और Compose State ऑब्जेक्ट्स के स्वचालित ट्रैकिंग का समर्थन करता है। किसी अतिरिक्त डिपेंडेंसी की आवश्यकता नहीं — लाइब्रेरी स्वचालित रूप से पता लगाती है कि प्रोजेक्ट में कौन से Jetpack घटक उपयोग किए जा रहे हैं और संबंधित डिटेक्टरों को सक्रिय करती है।
LeakCanary रिपोर्ट (LeakTrace) GC Root से लीक हुए ऑब्जेक्ट तक एक बहु-पंक्ति रेफरेंस चेन है। प्रत्येक पंक्ति क्लास और फ़ील्ड दिखाती है जिसके माध्यम से एक मज़बूत रेफरेंस गुज़रता है। डेवलपर को चेन को नीचे से ऊपर पढ़ना चाहिए: निचली पंक्ति लीक हुआ ऑब्जेक्ट है, ऊपरी पंक्ति प्रवेश बिंदु (GC Root) है।
एक विशिष्ट LeakTrace इस तरह दिखता है: GC Root → Application का स्टैटिक फ़ील्ड → सिंगलटन → कॉलबैक → Activity। यदि डेवलपर ऐसी चेन देखता है, समस्या स्पष्ट है: सिंगलटन एक कॉलबैक रखता है जिसने Activity का रेफरेंस कैप्चर किया है। समाधान — सिंगलटन में मज़बूत रेफरेंस को कमज़ोर रेफरेंस से बदलना है।
┬
├─ android.app.Application
│ Leaking: NO (Application — singleton)
│ ↓ Application.leakedActivities
├─ java.util.ArrayList
│ Leaking: NO (ArrayList — normal)
│ ↓ ArrayList[0]
├─ com.example.MainActivity
│ Leaking: YES (Activity destroyed but still in memory)
│ ↓ MainActivity.mCallback
├─ com.example.CallbackWrapper
│ Leaking: UNKNOWN
│ ↓ CallbackWrapper.mListener
│ ~~~~~~~~~~
├─ com.example.MyCallback (anonymous)
│ Leaking: UNKNOWN
│ ↓ MyCallback.this$0
├─ com.example.MainActivity
│ Leaking: YES (MainActivity is the leak)
╰
इस उदाहरण में, LeakCanary दिखाता है कि MainActivity चेन के माध्यम से धारित है: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → फिर MainActivity। this$0 तीर इंगित करता है कि अनाम क्लास MyCallback ने Activity का बाहरी रेफरेंस कैप्चर किया है। समाधान — कॉलबैक को कमज़ोर रेफरेंस बनाना या इसे onDestroy में रद्द करना है।
LeakCanary चेन में प्रत्येक तत्व के लिए लीक स्थिति भी दिखाता है: NO (कोई लीक नहीं — यह एक मूल तत्व है), YES (ऑब्जेक्ट को नष्ट किया जाना चाहिए), UNKNOWN (स्थिति निर्धारित नहीं की जा सकी)। UNKNOWN स्थिति का मतलब समस्या नहीं है — यह एक मध्यवर्ती ऑब्जेक्ट है जिसे LeakCanary निश्चित रूप से वर्गीकृत नहीं कर सकता।
संस्करण 1.x से 2.x में संक्रमण मौलिक था: डेवलपर्स ने लाइब्रेरी को शून्य से फिर से लिखा, पुराने HAHA विश्लेषक को Kotlin में लिखे अपने स्वयं के इंजन Shark से बदल दिया। Shark परिमाण के क्रम में तेज़ है, विश्लेषण के लिए कम मेमोरी की आवश्यकता है, और लीक के मूल कारणों को अधिक सटीक रूप से निर्धारित करता है।
| पैरामीटर | LeakCanary 1.x | LeakCanary 2.x |
|---|---|---|
| विश्लेषक भाषा | Java (HAHA — Android SDK का फोर्क) | Kotlin (Shark — स्वयं का इंजन) |
| सेटअप | Application में मैन्युअल AppWatcher कॉन्फ़िगरेशन | ContentProvider के माध्यम से स्वचालित |
| गति | हीप डंप विश्लेषण के लिए 10–30 सेकंड | हीप डंप विश्लेषण के लिए 1–5 सेकंड |
| प्रदर्शन | विश्लेषण के दौरान 10–50 MB RAM लेता है | विश्लेषण के दौरान 2–10 MB RAM लेता है |
Shark का मुख्य लाभ यह है कि यह पूरे हीप डंप को मेमोरी में लोड नहीं करता, बल्कि न्यूनतम आवंटन के साथ इसके रेफरेंस ग्राफ को ट्रैवर्स करता है। यह LeakCanary 2.x को विश्लेषण के दौरान OutOfMemoryError के जोखिम के बिना कम RAM वाले उपकरणों पर उपयोग के लिए उपयुक्त बनाता है।
संस्करण 2.x में Android Studio Memory Profiler में बाद के विश्लेषण के लिए हीप डंप निर्यात करने की क्षमता भी जोड़ी गई। ऐसा करने के लिए, AppWatcher कॉन्फ़िगरेशन में dumpHeapWhenLeakFound सेटिंग सक्षम करें।
LeakCanary Android के लिए सामान्य लीक की कई श्रेणियों का प्रभावी ढंग से पता लगाता है। सबसे आम है Activity पर स्टैटिक रेफरेंस के माध्यम से लीक — डेवलपर सिंगलटन में Activity कॉन्टेक्स्ट का रेफरेंस रखते हैं, और Activity अपने जीवनचक्र के समाप्त होने के बाद GC द्वारा एकत्र नहीं की जा सकती।
दूसरी सबसे आम श्रेणी अपंजीकृत श्रोताओं के माध्यम से लीक है। यदि onStart में registerListener कॉल किया गया था लेकिन onStop/onDestroy में unregisterListener कॉल नहीं किया गया, तो श्रोता ऑब्जेक्ट Activity नष्ट होने के बाद भी सिस्टम द्वारा धारित रहता है। LeakCanary स्पष्ट रूप से दिखाता है कि कौन सा श्रोता और किस सिस्टम सेवा में जीवित रहता है।
// सामान्य लीक: सिंगलटन कॉलबैक में Activity कैप्चर हुई
object AnalyticsManager {
private var callback: ((String) -> Unit)? = null
fun register(callback: (String) -> Unit) {
this.callback = callback // कॉलबैक पर मज़बूत रेफरेंस
}
fun unregister() {
callback = null // onDestroy में कॉल करना न भूलें!
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AnalyticsManager.register { event ->
logEvent(event) // लैम्ब्डा this कैप्चर करता है
}
// यदि onDestroy में unregister कॉल नहीं किया गया → Activity लीक
}
}
तीसरी श्रेणी BackStack में Fragment के माध्यम से लीक है। यदि FragmentTransaction.addToBackStack() को वापस जाते समय Fragment हटाए बिना कॉल किया जाता है, तो Fragment के पुराने इंस्टेंस मेमोरी में रहते हैं। LeakCanary विकास के शुरुआती चरणों में ऐसी छिपी लीक का पता लगाने में मदद करता है।
प्रत्येक पाई गई लीक के लिए, LeakCanary विवरण और ठीक करने की सिफारिशें प्रदान करता है। संस्करण 2.14 में Android Lint के साथ एकीकरण जोड़ा गया — लाइब्रेरी CI में लीक का पता चलने पर स्वचालित रूप से इश्यू ट्रैकर में कार्य बना सकती है।
अक्सर पूछे जाने वाले प्रश्न
हाँ, निश्चित रूप से। LeakCanary build.gradle में debugImplementation के माध्यम से जोड़ा जाता है, जो इसे रिलीज़ बिल्ड से स्वचालित रूप से बाहर करता है। यदि implementation का उपयोग किया जाता है, तो लाइब्रेरी रिलीज़ APK में शामिल हो जाएगी और अंतिम उपयोगकर्ताओं को लीक दिखाएगी — यह अस्वीकार्य है।
प्रदर्शन पर प्रभाव न्यूनतम है। LeakCanary केवल घटक के onDestroy के बाद सक्रिय होता है और UI रेंडरिंग या टच हैंडलिंग में हस्तक्षेप नहीं करता। एकमात्र लागत थोड़ी फोर्स्ड GC पॉज़ (लगभग 100 ms) और लीक होने पर हीप डंप लिखना (सेकंड का अंश) है।
LeakCanary स्वचालित रूप से हीप डंप को HPROF प्रारूप में एप्लिकेशन फ़ोल्डर में सहेजता है। फ़ाइल को Android Studio के माध्यम से निर्यात किया जा सकता है: Device File Explorer → data/data/com.example/files/leakcanary/. इसे देखने के लिए, Capture → Open Heap Dump के माध्यम से Memory Profiler में फ़ाइल खोलें।
हाँ, संस्करण 2.12 से LeakCanary पूरी तरह से Jetpack Compose का समर्थन करता है। लाइब्रेरी Composition कॉन्टेक्स्ट और State ऑब्जेक्ट्स को ट्रैक करती है, Composable फ़ंक्शन में स्वचालित रूप से लीक का पता लगाती है। किसी अलग कॉन्फ़िगरेशन की आवश्यकता नहीं — यह बॉक्स से बाहर काम करता है।
गलत सकारात्मक संभव हैं लेकिन दुर्लभ। LeakCanary लीक घोषित करने से पहले तीन बार GC कॉल का उपयोग करता है, जो अधिकांश गलत सकारात्मक को समाप्त करता है। यदि आपको लगता है कि पहचान गलत है, तो कॉन्फ़िगरेशन में विशिष्ट क्लास के लिए IgnoredReference बनाएँ।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें