OutOfMemoryError एक घातक अपवाद है जो तब होता है जब Java वर्चुअल मशीन (JVM) या Android Runtime (ART) Heap में पर्याप्त स्थान न होने के कारण किसी नए ऑब्जेक्ट के लिए मेमोरी आवंटित नहीं कर पाती। Square Engineering के अनुसार, मोबाइल एप्लिकेशन में 70% OutOfMemoryError मेमोरी लीक के कारण होते हैं, न कि वास्तविक सीमा से अधिक होने के कारण। OOM के कारणों को समझना एप्लिकेशन की स्थिरता की कुंजी है।
मुख्य बिंदु
OutOfMemoryError (OOM) Java/Kotlin में VirtualMachineError परिवार का एक अपवाद है जो नए ऑब्जेक्ट के लिए मेमोरी आवंटित करने में असमर्थता का संकेत देता है। चेक किए गए अपवादों के विपरीत, OOM एक Error है और इसे catch के माध्यम से संभालने की आवश्यकता नहीं है — हालांकि इसे तकनीकी रूप से पकड़ा जा सकता है। OOM होने के बाद, एप्लिकेशन आमतौर पर अस्थिर स्थिति में होता है और इसे समाप्त करने की अनुशंसा की जाती है।
Android पर, प्रत्येक एप्लिकेशन की एक Heap सीमा होती है जो डिवाइस निर्माता द्वारा निर्धारित की जाती है। 6+ जीबी RAM वाले आधुनिक स्मार्टफोन के लिए, सीमा 256–512 एमबी है, बजट डिवाइस के लिए — 128–192 एमबी। जब सभी जीवित ऑब्जेक्ट की कुल मात्रा इस सीमा से अधिक हो जाती है, तो ART OutOfMemoryError फेंकता है।
यह समझना महत्वपूर्ण है: OOM का हमेशा यह मतलब नहीं है कि डिवाइस में भौतिक मेमोरी खत्म हो गई है। इसका मतलब है कि एप्लिकेशन ने सिस्टम द्वारा निर्धारित अपनी Heap सीमा समाप्त कर दी है। अन्य एप्लिकेशन के पास खाली मेमोरी हो सकती है, लेकिन आपका एप्लिकेशन Android में प्रक्रिया अलगाव के कारण इसका उपयोग नहीं कर सकता।
पाँच परिदृश्य नियमित रूप से मोबाइल एप्लिकेशन में OOM का कारण बनते हैं। प्रत्येक परिदृश्य एक विशिष्ट डेटा प्रकार या संचालन से जुड़ा होता है।
Bitmap Android एप्लिकेशन में मुख्य मेमोरी उपभोक्ता है। FullHD छवि (1920 × 1080) को मूल आकार में लोड करने पर ARGB_8888 प्रारूप में 8.3 एमबी लगता है। यदि RecyclerView में ऐसी 50 छवियाँ हैं — तो यह 415 एमबी है, जो किसी भी डिवाइस के Heap से अधिक है। inSampleSize के बिना छवियाँ लोड करना कमज़ोर डिवाइस पर गारंटीड OOM है।
स्वचालित स्केलिंग के लिए Glide या Coil का उपयोग करें। ये लाइब्रेरीज़ मूल रिज़ॉल्यूशन के बजाय View से मेल खाने वाले आकार में छवियाँ लोड करती हैं। BitmapFactory.Options के सीधे उपयोग के लिए inSampleSize लागू करें: इसे दो की घात के रूप में गणना करें ताकि अंतिम आकार 2048 × 2048 पिक्सेल से अधिक न हो। अतिरिक्त रूप से, पारदर्शिता के बिना छवियों के लिए ARGB_8888 के बजाय RGB_565 का उपयोग करें — इससे मेमोरी खपत आधी हो जाती है।
fun loadScaledBitmap(path: String, reqWidth: Int): Bitmap? {
val opts = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeFile(path, opts)
opts.inSampleSize = calculateSampleSize(opts.outWidth, reqWidth)
opts.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, opts)
}
एक कुछ KB का लीक OOM का कारण नहीं बनेगा। लेकिन प्रत्येक स्क्रीन पर दर्जनों लीक जमा होते हैं: प्रत्येक स्क्रीन संक्रमण एक लीक जोड़ता है, GC ऑब्जेक्ट को मुक्त नहीं कर पाता, और Heap भर जाता है। एक विशिष्ट पैटर्न: उपयोगकर्ता प्रोफ़ाइल स्क्रीन को 20 बार खोलता और बंद करता है → Heap 200 एमबी बढ़ता है → एप्लिकेशन OOM के साथ क्रैश होता है।
स्वचालित लीक पहचान के लिए प्रोजेक्ट में LeakCanary स्थापित करें। यह सटीक स्टैक ट्रेस के साथ प्रत्येक लीक ऑब्जेक्ट दिखाएगा। सभी लीक ठीक करने के बाद, Heap खपत स्थिर हो जाती है: स्क्रीन बंद करने के बाद, मेमोरी आधार स्तर पर वापस आ जाती है।
पूरी फ़ाइलों को byte[] में लोड करना OOM का सीधा रास्ता है। 50 एमबी की JSON फ़ाइल पार्सिंग के दौरान समान आकार की एक स्ट्रिंग और एक DOM मॉडल बनाएगी। मेमोरी में लोड की गई वीडियो फ़ाइलें, ऑडियो बफ़र और बड़े protobuf डेटासेट — ये सभी एक ही संचालन में Heap सीमा से अधिक हो सकते हैं।
बड़े डेटा को स्ट्रीम का उपयोग करके प्रोसेस करें: 4–8 KB बफ़र के साथ InputStream, स्ट्रीमिंग JSON पार्सर (Jackson या Gson JsonReader के साथ), वीडियो के लिए MediaCodec। उपलब्ध Heap के 10% से अधिक आकार वाली फ़ाइलों पर कभी File.readBytes() कॉल न करें।
लूप में बिना मध्यवर्ती GC के गहन ऑब्जेक्ट निर्माण OOM का कारण बन सकता है, विशेष रूप से छोटे Heap वाले डिवाइस पर। उदाहरण: for-loop में 100,000 ऑब्जेक्ट उत्पन्न करना जो GC द्वारा एकत्र किए जाने से पहले Heap में नहीं समाते। यह गेम और ग्राफ़िक्स संपादकों में अधिक सामान्य है।
उन ऑब्जेक्ट के लिए Object Pool का उपयोग करें जो बड़े पैमाने पर बनाए और नष्ट किए जाते हैं। संख्यात्मक डेटा के लिए प्रिमिटिव का उपयोग करें (List<Float> के बजाय FloatArray)। ViewHolder Pool के साथ RecyclerView UI घटकों के लिए इस समस्या को हल करता है।
विखंडन एक ऐसी स्थिति है जहाँ कुल मिलाकर पर्याप्त खाली मेमोरी है, लेकिन नए ऑब्जेक्ट के लिए कोई सन्निकट ब्लॉक नहीं है। ART GC के दौरान Heap को संकुचित करता है, लेकिन हमेशा सफलतापूर्वक नहीं। बड़े ऐरे (Bitmap, byte[]) विखंडन के प्रति सबसे अधिक संवेदनशील होते हैं।
Android 8+ पर ART Generational GC का उपयोग करता है, जो युवा और पुराने ऑब्जेक्ट को अलग करके विखंडन को कम करता है। फिर भी, एक ही पूल में विभिन्न आकार के टुकड़े आवंटित करने से बचें — पूर्व-आवंटित निश्चित आकार के बफ़र का उपयोग करने का प्रयास करें।
Android में Heap सीमा एक स्थिरांक नहीं है — यह निर्माता, डिवाइस मॉडल और OS संस्करण पर निर्भर करता है। Google Compatibility Definition Document (CDD) के माध्यम से न्यूनतम आवश्यकताएँ निर्धारित करता है, लेकिन निर्माता वास्तविक मान निर्धारित करते हैं।
| डिवाइस श्रेणी | सामान्य Heap | largeHeap |
|---|---|---|
| बजट (1–2 जीबी RAM) | 128–192 एमबी | 256–384 एमबी |
| मध्य-श्रेणी (3–4 जीबी RAM) | 256–384 एमबी | 512 एमबी |
| फ्लैगशिप (6+ जीबी RAM) | 384–512 एमबी | 768 एमबी–1 जीबी |
| टैबलेट (4+ जीबी RAM) | 256–512 एमबी | 768 एमबी |
| Wear OS | 32–64 एमबी | उपलब्ध नहीं |
आप मेनिफ़ेस्ट में android:largeHeap="true" के माध्यम से बढ़ी हुई सीमा का अनुरोध कर सकते हैं। इसका सावधानी से उपयोग करें: Heap बढ़ाने से लीक की समस्या हल नहीं होती और यदि सिस्टम आपके एप्लिकेशन के लिए मेमोरी मुक्त करने हेतु अन्य एप्लिकेशन को मारने के लिए मजबूर होता है तो उपयोगकर्ता अनुभव खराब हो सकता है। Wear OS के लिए, Heap सीमा न्यूनतम है — केवल 32–64 एमबी, यहाँ largeHeap उपलब्ध नहीं है, और मेमोरी बचत दोगुनी महत्वपूर्ण है।
OOM का निदान करने के लिए Heap Dump का विश्लेषण और यह समझना आवश्यक है कि कौन से ऑब्जेक्ट मेमोरी का उपभोग कर रहे हैं। Android Studio सभी आवश्यक उपकरण प्रदान करता है।
चरण 1: OOM क्षण को कैप्चर करें। Android Memory Profiler में, Record memory allocations पर क्लिक करें और वह परिदृश्य निष्पादित करें जो क्रैश का कारण बनता है। Profiler OOM से पहले आवंटन में वृद्धि दिखाएगा। यदि OOM पुनरुत्पादन योग्य नहीं है, तो डीबग बिल्ड में android:smallHeap के माध्यम से Heap कम करें या मैन्युअल GC कॉल के साथ DDMS का उपयोग करें।
चरण 2: पीक लोड पर (OOM से पहले) Heap Dump लें। Android Studio में Dump खोलें: Classes टैब Retained Size के अनुसार क्रमबद्ध है। सबसे बड़े ऑब्जेक्ट Bitmap, byte[], String हैं। प्रत्येक Bitmap के लिए, आकार (चौड़ाई × ऊँचाई × 4 बाइट) और Stack Trace के माध्यम से लोड पथ देखें।
चरण 3: डुप्लिकेट ऑब्जेक्ट की संख्या का विश्लेषण करें। यदि आप 200 समान Fragment या Activity देखते हैं — यह एक लीक है। यदि समान आकार वाले 500 Bitmap — यह एक छवि कैशिंग समस्या है। MAT (Memory Analyzer Tool) Dominator Tree के साथ गहन विश्लेषण प्रदान करता है जो दिखाता है कि कौन से ऑब्जेक्ट 80% Heap रखते हैं।
// adb के माध्यम से Heap Dump कमांड
adb shell am dumpheap com.example.app /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof.
एक व्यापक OOM रोकथाम रणनीति में सुरक्षा के पाँच स्तर शामिल हैं: वास्तुशिल्प निर्णयों से लेकर उत्पादन निगरानी तक।
ViewModel + Repository पैटर्न डेटा को UI से अलग करता है और स्क्रीन रोटेशन पर View को धारण करने से रोकता है। ViewModel Activity से अधिक जीवित रहता है, इसका डेटा खोता नहीं है, और View को मेमोरी में डेटा डुप्लिकेट किए बिना पुनः बनाया जा सकता है। स्पष्ट स्थिति प्रबंधन के लिए LiveData के बजाय StateFlow का उपयोग करें।
Glide छवियों के साथ काम करने के लिए एक अनिवार्य लाइब्रेरी है। यह स्वचालित रूप से स्केल, कैश (डिस्क + मेमोरी) और Bitmap को रीसाइकिल करता है। बड़ी सूचियों के लिए diskCacheStrategy और skipMemoryCache कॉन्फ़िगर करें। एनिमेटेड छवियों के लिए, GIF/WebP के साथ Glide का उपयोग करें — वे Bitmap के अनुक्रम की तुलना में कम मेमोरी लेते हैं।
Firebase Performance Monitoring वास्तविक समय में मेमोरी खपत को ट्रैक करता है। Heap उपयोग 80% से अधिक होने पर अलर्ट सेट करें — यह जाँच का संकेत है। Crashlytics OOM को एक अपवाद के रूप में एकत्र करता है और क्रैश से पहले अंतिम ज्ञात Heap स्थिति दिखाता है। Android 11+ के लिए, OOM समाप्ति का पता लगाने के लिए ApplicationExitInfo का उपयोग करें।
सुनिश्चित करें कि आप न्यूनतम Heap (128–192 एमबी) वाले डिवाइस पर एप्लिकेशन का परीक्षण करें। छोटी स्क्रीन और छोटे Heap वाला एमुलेटर बजट डिवाइस का अनुकरण करता है। यदि एप्लिकेशन ऐसे डिवाइस पर काम करता है, तो फ्लैगशिप पर OOM समस्याएँ नहीं होंगी। विभिन्न मूल्य श्रेणियों के वास्तविक डिवाइस के साथ Firebase Test Lab का उपयोग करें।
// भारी संचालन से पहले उपलब्ध Heap की जाँच
fun canAllocate(requiredBytes: Long): Boolean {
val runtime = Runtime.getRuntime()
val free = runtime.freeMemory()
return free > requiredBytes * 2 // 50% बफ़र
}
अक्सर पूछे जाने वाले प्रश्न
तकनीकी रूप से हाँ, लेकिन यह अनुशंसित नहीं है। OOM के बाद, एप्लिकेशन अस्थिर स्थिति में होता है: नए आवंटन विफल हो सकते हैं, और कुछ ऑब्जेक्ट आंशिक रूप से बन सकते हैं। catch में एकमात्र उचित कार्रवाई लॉगिंग और Activity को पुनः आरंभ करना है।
Heap सीमा डिवाइसों के बीच भिन्न होती है। एक संचालन जिसे 300 एमबी की आवश्यकता है, वह 192 एमबी सीमा वाले डिवाइस पर विफल होगा लेकिन 512 एमबी वाले फ्लैगशिप पर सफल होगा। OOM परिदृश्यों का पता लगाने के लिए न्यूनतम विनिर्देशों वाले डिवाइस पर परीक्षण करें।
largeHeap सीमा बढ़ाता है लेकिन एप्लिकेशन को गति नहीं देता। GC विराम लंबे हो जाते हैं क्योंकि बड़े Heap को इकट्ठा करने में अधिक समय लगता है। सिस्टम मेमोरी प्रदान करने के लिए पृष्ठभूमि एप्लिकेशन को मार सकता है। largeHeap का उपयोग केवल उन एप्लिकेशन के लिए करें जिन्हें वस्तुनिष्ठ रूप से बहुत अधिक मेमोरी की आवश्यकता है (कैमरे, संपादक)।
OOM Heap अपर्याप्त होने पर एप्लिकेशन के अंदर एक अपवाद है। सिस्टम किल (Low Memory Killer) अन्य एप्लिकेशन के लिए मेमोरी मुक्त करने हेतु प्रक्रिया को मारने का Linux कर्नेल का निर्णय है। सिस्टम किल में, एप्लिकेशन को अपवाद नहीं मिलता — प्रक्रिया बस समाप्त हो जाती है।
सूत्र: चौड़ाई × ऊँचाई × bytesPerPixel। ARGB_8888 = 4 बाइट/पिक्सेल, RGB_565 = 2 बाइट/पिक्सेल। ARGB_8888 में FullHD Bitmap (1920 × 1080) = 8.3 एमबी। 4K Bitmap (3840 × 2160) = 33 एमबी। छवियों को हमेशा स्क्रीन पर प्रदर्शन के लिए आवश्यक आकार में स्केल करें।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें