इस पेज पर, अपने ऐप्लिकेशन में मेमोरी के इस्तेमाल को कम करने का तरीका बताया गया है. Android ऑपरेटिंग सिस्टम, मेमोरी को कैसे मैनेज करता है, यह जानने के लिए, मेमोरी मैनेजमेंट की खास जानकारी देखें.
रैंडम-ऐक्सेस मेमोरी (रैम), सॉफ़्टवेयर डेवलपमेंट के किसी भी एनवायरमेंट के लिए एक अहम संसाधन है. यह मोबाइल ऑपरेटिंग सिस्टम के लिए और भी अहम है, क्योंकि इसमें अक्सर फ़िज़िकल मेमोरी सीमित होती है. Android Runtime (ART) और Dalvik वर्चुअल मशीन, दोनों ही रूटीन गार्बेज कलेक्शन की प्रोसेस पूरी करते हैं. हालांकि, इसका मतलब यह नहीं है कि आपके ऐप्लिकेशन के लिए, मेमोरी कब और कहां से ऐलोकेट और रिलीज़ की जाती है, इस बारे में आपको जानकारी नहीं होनी चाहिए. आपको अब भी मेमोरी लीक होने से बचना होगा. आम तौर पर, ऐसा तब होता है, जब स्टैटिक मेंबर वैरिएबल में ऑब्जेक्ट रेफ़रंस सेव किए जाते हैं. साथ ही, लाइफ़साइकल कॉलबैक के हिसाब से, Reference
ऑब्जेक्ट को सही समय पर रिलीज़ करना होगा.
अपने ऐप्लिकेशन के कोड और संसाधन के फ़ुटप्रिंट को कम करना
आपके कोड में मौजूद कुछ संसाधन और लाइब्रेरी, आपकी जानकारी के बिना भी मेमोरी का इस्तेमाल कर सकती हैं. आपके ऐप्लिकेशन का कुल साइज़, जिसमें तीसरे पक्ष की लाइब्रेरी या एम्बेड किए गए संसाधन शामिल हैं, इस बात पर असर डाल सकता है कि आपका ऐप्लिकेशन कितनी मेमोरी का इस्तेमाल करता है. अपने कोड से, ज़रूरत से ज़्यादा, गैर-ज़रूरी या बहुत बड़े कॉम्पोनेंट, संसाधन, और लाइब्रेरी हटाकर, अपने ऐप्लिकेशन की मेमोरी के इस्तेमाल को बेहतर बनाया जा सकता है.
R8 को चालू करके, ऐप्लिकेशन का कुल साइज़ कम करना
कंपाइल किया गया आपका ऐप्लिकेशन कोड, रनटाइम मेमोरी फ़ुटप्रिंट का एक अहम हिस्सा होता है. हर क्लास, मेथड, लाइब्रेरी डिपेंडेंसी, और स्ट्रिंग कॉन्स्टैंट को रनटाइम में रैम में लोड करना ज़रूरी है. आपका कंपाइल किया गया कोड बेस जितना बड़ा होगा, आपके ऐप्लिकेशन को उतनी ही ज़्यादा फ़िज़िकल रैम की ज़रूरत होगी.
अपने ऐप्लिकेशन के मेमोरी फ़ुटप्रिंट को कम करने के लिए, R8 का इस्तेमाल किया जा सकता है. R8 को आम तौर पर, एपीके का साइज़ कम करने के लिए जाना जाता है. हालांकि, इसका रनटाइम मेमोरी (रैम) पर सीधा और सकारात्मक असर पड़ता है. R8, आपके ऐप्लिकेशन के बाइट कोड का विश्लेषण करके, इस्तेमाल न होने वाले कोड को हटाता है, ज़रूरत से ज़्यादा क्लास को मर्ज करता है, इनलाइन मेथड का इस्तेमाल करता है, और आइडेंटिफ़ायर को छोटा करता है. एपीके से रैम में कंपाइल किए गए कम बाइटकोड लोड करके, यह ऐप्लिकेशन के कुल बेसलाइन मेमोरी फ़ुटप्रिंट को कम करता है. इसके अलावा, क्लास, मेथड, और फ़ील्ड के नामों को छोटे आइडेंटिफ़ायर में बदलकर, रैम के ओवरहेड को सीधे तौर पर कम किया जाता है. क्लास मर्जिंग और एक्सटेंसिव मेथड इनलाइनिंग जैसे ऑप्टिमाइज़ेशन, महंगे रनटाइम लुकअप और ऐलोकेशन पैटर्न को भी बदलते हैं. इससे हीप और स्टैक मेमोरी ऑप्टिमाइज़ होती है.
कीप रूल समझना
कीप रूल, कॉन्फ़िगरेशन के ऐसे निर्देश होते हैं जो R8 को बताते हैं कि ऑप्टिमाइज़ेशन के दौरान, आपके कोड के किन हिस्सों को सेव रखना है. इससे, R8 आपके ऐप्लिकेशन के लिए ज़रूरी कोड को हटाने या छोटा करने से बचता है. ज़्यादा जानकारी के लिए, कीप रूल की खास जानकारी देखें.
कीप रूल सही तरीके से न लिखने पर, R8 आपके कोडबेस के बड़े हिस्सों को ऑप्टिमाइज़ नहीं कर पाता. ज़्यादा ब्रॉड कीप रूल इस्तेमाल न करें और इन सबसे सही तरीकों को अपनाएं:
- ग्लोबल रूल जिनसे बचना चाहिए:
-dontoptimize: इससे पूरे ऐप्लिकेशन के लिए ऑप्टिमाइज़ेशन की सुविधा पूरी तरह से बंद हो जाती है. इससे, ऐप्लिकेशन के साइज़ में बढ़ोतरी होती है और वह धीरे चलता है.-dontshrink: इससे इस्तेमाल न होने वाले कोड और संसाधनों को हटाने से रोका जाता है.-dontobfuscate: इससे नाम को छोटा करने से रोका जाता है. इससे मेमोरी की बचत नहीं हो पाती. खास तौर पर, बड़े ऐप्लिकेशन में.
पूरे पैकेज के लिए वाइल्डकार्ड इस्तेमाल न करें: ब्रॉड रूल जैसे
-keep class com.example.package.** { *; }R8 को उस पैकेज में मौजूद हर क्लास, फ़ील्ड, और मेथड को सेव रखने के लिए मजबूर करते हैं. इससे, R8 उस पैकेज में मौजूद कोड को हटाने, ऑप्टिमाइज़ करने या छोटा करने की सुविधा का इस्तेमाल नहीं कर पाता.R8 की डिफ़ॉल्ट कॉन्फ़िगरेशन फ़ाइल का इस्तेमाल करें: हमेशा
proguard-android-optimize.txtका इस्तेमाल करें.
कीप रूल लिखने के बारे में ज़्यादा जानकारी के लिए, कीप रूल की खास जानकारी देखें. इस्तेमाल किए जाने वाले और इस्तेमाल न किए जाने वाले खास पैटर्न के बारे में जानने के लिए, कीप रूल के सबसे सही तरीके देखें.
R8 कॉन्फ़िगरेशन एनालाइज़र, आपके R8 कॉन्फ़िगरेशन और हर कीप रूल के आपके ऐप्लिकेशन पर पड़ने वाले असर के बारे में जानकारी देता है. ऑप्टिमाइज़ेशन को ब्लॉक करने वाले नियमों की पहचान करने के बारे में ज़्यादा जानकारी के लिए, R8 कॉन्फ़िगरेशन एनालाइज़र देखें.
बाहरी लाइब्रेरी का इस्तेमाल करते समय सावधानी बरतें
बाहरी लाइब्रेरी का कोड अक्सर मोबाइल एनवायरमेंट के लिए नहीं लिखा जाता. इसलिए, मोबाइल क्लाइंट पर काम करने के लिए, यह सही नहीं होता. बाहरी लाइब्रेरी का इस्तेमाल करते समय, आपको मोबाइल डिवाइसों के लिए उस लाइब्रेरी को ऑप्टिमाइज़ करना पड़ सकता है. इस काम की योजना पहले से बनाएं और लाइब्रेरी का इस्तेमाल करने से पहले, कोड के साइज़ और रैम फ़ुटप्रिंट के हिसाब से उसका विश्लेषण करें.
मोबाइल के लिए ऑप्टिमाइज़ की गई कुछ लाइब्रेरी भी, अलग-अलग तरीके से लागू करने की वजह से समस्याएं पैदा कर सकती हैं. उदाहरण के लिए, हो सकता है कि एक लाइब्रेरी, लाइट प्रोटोबफ़ का इस्तेमाल करे, जबकि दूसरी माइक्रो प्रोटोबफ़ का इस्तेमाल करे. इससे आपके ऐप्लिकेशन में प्रोटोबफ़ के दो अलग-अलग तरीके लागू हो सकते हैं. ऐसा लॉगिंग, Analytics, इमेज-लोडिंग फ़्रेमवर्क, कैशिंग, और कई अन्य चीज़ों के अलग-अलग तरीके लागू करने पर हो सकता है.
R8 का इस्तेमाल करके अपने ऐप्लिकेशन को ऑप्टिमाइज़ करने से, डिपेंडेंसी से इस्तेमाल न होने वाले कोड को हटाया जा सकता है. हालांकि, इसकी असरदार क्षमता अक्सर लाइब्रेरी के इंटरनल कॉन्फ़िगरेशन से सीमित होती है. उदाहरण के लिए, ब्रॉड कीप रूल या किसी लाइब्रेरी में रिफ़्लेक्शन का इस्तेमाल करने से, R8 उसके कोड को छोटा नहीं कर पाता. इससे मेमोरी फ़ुटप्रिंट बढ़ जाता है. असरदार लाइब्रेरी चुनने की रणनीतियों के लिए, लाइब्रेरी सोच-समझकर चुनें देखें.
कुछ खास सुविधाओं के लिए, शेयर की गई लाइब्रेरी का इस्तेमाल न करें. इस्तेमाल न होने वाले कोड और ओवरहेड को न जोड़ें. किसी लाइब्रेरी का इस्तेमाल करने के बारे में सोचते समय, ऐसी लाइब्रेरी देखें जो आपकी ज़रूरत के हिसाब से काम करे. इसके अलावा, आपके पास अपना तरीका बनाने का विकल्प भी है.
डिपेंडेंसी इंजेक्शन के लिए, Hilt या Dagger 2 का इस्तेमाल करना
डिपेंडेंसी इंजेक्शन फ़्रेमवर्क, आपके लिखे गए कोड को आसान बना सकते हैं. साथ ही, एक अडैप्टिव एनवायरमेंट उपलब्ध करा सकते हैं. यह टेस्टिंग और कॉन्फ़िगरेशन में अन्य बदलावों के लिए काम का होता है.
अगर आपको अपने ऐप्लिकेशन में डिपेंडेंसी इंजेक्शन फ़्रेमवर्क का इस्तेमाल करना है, तो Hilt या Dagger का इस्तेमाल करें. Hilt, Android के लिए डिपेंडेंसी इंजेक्शन लाइब्रेरी है. यह Dagger के ऊपर काम करती है. Dagger, आपके ऐप्लिकेशन के कोड को स्कैन करने के लिए रिफ़्लेक्शन का इस्तेमाल नहीं करता. Android ऐप्लिकेशन में, Dagger के स्टैटिक कंपाइल-टाइम तरीके को लागू किया जा सकता है. इसके लिए, रनटाइम में किसी तरह का खर्च या मेमोरी का इस्तेमाल नहीं करना पड़ता.
रिफ़्लेक्शन का इस्तेमाल करने वाले अन्य डिपेंडेंसी इंजेक्शन फ़्रेमवर्क, एनोटेशन के लिए आपके कोड को स्कैन करके प्रोसेस शुरू करते हैं. इस प्रोसेस में, सीपीयू साइकल और रैम का ज़्यादा इस्तेमाल हो सकता है. साथ ही, ऐप्लिकेशन लॉन्च होने पर, लैग हो सकता है.
डिपेंडेंसी इंजेक्शन का इस्तेमाल करते समय, यह पक्का करें कि ऑब्जेक्ट को सही तरीके से स्कोप किया गया हो, ताकि मेमोरी लीक न हो. ऑब्जेक्ट को गलत लाइफ़साइकल से बाइंड करने पर, उन्हें ज़रूरत से ज़्यादा समय तक सेव रखा जा सकता है. इससे मेमोरी लीक हो सकती है.
इमेज लोड करते समय सावधानी बरतें
आम तौर पर, ग्राफ़िक बिटमैप, आपके ऐप्लिकेशन की मेमोरी में मौजूद सबसे बड़े ऑब्जेक्ट होते हैं. अगर आप JPEG जैसी कंप्रेस की गई फ़ाइलों के साथ काम कर रहे हैं, तो भी स्क्रीन पर दिखाने के लिए, फ़ाइल को कंप्रेस न किए गए बिटमैप में बदलना होगा. कंप्रेस की गई छोटी इमेज फ़ाइल, बहुत बड़े बिटमैप में बदल सकती है.
उदाहरण के लिए, ज़्यादातर बिटमैप, ARGB_8888 कॉन्फ़िगरेशन का इस्तेमाल करते हैं. इसका मतलब है कि हर पिक्सल के लिए, चार बाइट मेमोरी की ज़रूरत होती है. इसमें लाल, हरे, नीले, और ऐल्फ़ा (पारदर्शिता) के लिए एक-एक बाइट होती है. अगर आपके पास 100 केबी की JPEG इमेज है और उसे 1000×1000 पिक्सल व्यू में दिखाया जाता है, तो बिटमैप के लिए 10 लाख पिक्सल में से हर पिक्सल के लिए चार बाइट की ज़रूरत होगी. इससे 4 एमबी मेमोरी का इस्तेमाल होगा.
इमेज के इस्तेमाल को ऑप्टिमाइज़ करने के लिए, कई तरीके अपनाए जा सकते हैं. उदाहरण के लिए, इमेज लोडिंग लाइब्रेरी का इस्तेमाल करके, ज़रूरत न होने पर मेमोरी रिलीज़ की जा सकती है. इमेज को असरदार तरीके से मैनेज करने के बारे में जानकारी के लिए, बिटमैप इमेज को ऑप्टिमाइज़ करना देखें.
उपलब्ध मेमोरी और मेमोरी के इस्तेमाल की निगरानी करना
अपने ऐप्लिकेशन में मेमोरी के इस्तेमाल से जुड़ी समस्याओं को ठीक करने से पहले, आपको उनका पता लगाना होगा. Android Studio में मौजूद मेमोरी प्रोफ़ाइलर, मेमोरी से जुड़ी समस्याओं को ढूंढने और उनकी पहचान करने में आपकी मदद करता है. इसके लिए, वह इन तरीकों का इस्तेमाल करता है:
- देखें कि समय के साथ आपका ऐप्लिकेशन, मेमोरी कैसे ऐलोकेट करता है. मेमोरी प्रोफ़ाइलर, रीयलटाइम में एक ग्राफ़ दिखाता है. इससे पता चलता है कि आपका ऐप्लिकेशन कितनी मेमोरी का इस्तेमाल कर रहा है, ऐलोकेट किए गए Java ऑब्जेक्ट की संख्या कितनी है, और गार्बेज कलेक्शन कब होता है.
- गार्बेज कलेक्शन इवेंट शुरू करें और आपका ऐप्लिकेशन रन होने के दौरान, Java हीप का स्नैपशॉट लें.
- अपने ऐप्लिकेशन के मेमोरी ऐलोकेशन रिकॉर्ड करें, ऐलोकेट किए गए सभी ऑब्जेक्ट की जांच करें, हर ऐलोकेशन के लिए स्टैक ट्रेस देखें, और उससे जुड़े कोड पर जाएं Android Studio एडिटर में.
मेमोरी प्रोफ़ाइलर, LeakCanary लीक डिटेक्शन लाइब्रेरी के साथ भी इंटिग्रेट होता है. LeakCanary का इस्तेमाल करके, टेस्ट डिवाइस से डेवलपमेंट मशीन पर मेमोरी लीक के विश्लेषण को ट्रांसफ़र किया जा सकता है. इससे आपके वर्कफ़्लो की स्पीड बढ़ सकती है. ज़्यादा जानकारी के लिए, Android Studio के रिलीज़ नोट देखें.
मेमोरी से जुड़ी समस्याओं की पहचान करने के लिए, अन्य टूल का इस्तेमाल किया जा सकता है. ये टूल, प्रोडक्शन में चल रहे आपके ऐप्लिकेशन के उपयोगकर्ताओं के डेटा पर आधारित होते हैं:
- कम मेमोरी की वजह से प्रोसेस बंद करने (एलएमके) वाले इवेंट ट्रैक करने के लिए, Android Vitals का इस्तेमाल करें.
- मेमोरी खत्म होने की वजह से होने वाली गड़बड़ियों के साथ-साथ, ऐप्लिकेशन के अनियमित व्यवहार को ट्रैक करने के लिए, प्रोफ़ाइलिंग मैनेजर का इस्तेमाल करें. ऐसा हो सकता है कि मेमोरी लीक होने की वजह से, ऐप्लिकेशन का व्यवहार अनियमित हो.
इवेंट के जवाब में मेमोरी रिलीज़ करना
Android, आपके ऐप्लिकेशन से मेमोरी वापस ले सकता है या आपके ऐप्लिकेशन को पूरी तरह से बंद कर सकता है. ऐसा तब किया जाता है, जब ज़रूरी टास्क के लिए मेमोरी खाली करनी हो
. इसके बारे में, मेमोरी
मैनेजमेंट की खास जानकारी में बताया गया है. सिस्टम मेमोरी को बैलेंस करने और सिस्टम को आपके ऐप्लिकेशन की प्रोसेस को बंद करने से रोकने के लिए, ComponentCallbacks2
इंटरफ़ेस को अपनी Activity क्लास में लागू किया जा सकता है. प्रोवाइड किया गया onTrimMemory()
कॉलबैक मेथड, आपके ऐप्लिकेशन को लाइफ़साइकल या मेमोरी से जुड़े इवेंट के बारे में सूचना देता है. इससे आपके ऐप्लिकेशन को अपनी मेमोरी के इस्तेमाल को कम करने का मौका मिलता है.
मेमोरी खाली करने से, कम मेमोरी की वजह से प्रोसेस बंद करने वाले टूल से आपके ऐप्लिकेशन को बंद करने की संभावना कम हो सकती है.
onTrimMemory() को लागू करते समय, सिर्फ़ TRIM_MEMORY_UI_HIDDEN और TRIM_MEMORY_BACKGROUND इवेंट पर फ़ोकस करें. (Android 14 से, सिस्टम अब अन्य, लेगसी कॉन्स्टैंट के लिए सूचनाएं नहीं देता. Android 15 में, उन कॉन्स्टैंट को आधिकारिक तौर पर बंद कर दिया गया है.)
TRIM_MEMORY_UI_HIDDEN: इस सिग्नल से पता चलता है कि आपके ऐप्लिकेशन का यूज़र इंटरफ़ेस (यूआई), उपयोगकर्ता के व्यू से बाहर चला गया है. इस ट्रांज़िशन से, यूज़र इंटरफ़ेस (यूआई) से जुड़े मेमोरी ऐलोकेशन को रिलीज़ करने का मौका मिलता है. जैसे, बिटमैप, वीडियो प्लेबैक बफ़र या जटिल ऐनिमेशन संसाधन.TRIM_MEMORY_BACKGROUND: इस सिग्नल से पता चलता है कि आपकी प्रोसेस बैकग्राउंड में चल रही है और अब सिस्टम की ग्लोबल मेमोरी की ज़रूरतों को पूरा करने के लिए, इसे बंद किया जा सकता है. अपनी प्रोसेस को कैश मेमोरी में सेव रखने की अवधि बढ़ाने और ऐप्लिकेशन के कोल्ड स्टार्ट की संख्या कम करने के लिए, आपको उन सभी संसाधनों को रिलीज़ करना चाहिए जिन्हें उपयोगकर्ता के सेशन फिर से शुरू करने पर आसानी से फिर से बनाया जा सकता है.
इस कोड के सैंपल में, मेमोरी से जुड़े अलग-अलग इवेंट के जवाब में, onTrimMemory() कॉलबैक को लागू करने का तरीका बताया गया है:
Kotlin
import android.content.ComponentCallbacks2
// Other import statements.
class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
// Other activity code.
/**
* Release memory when the UI becomes hidden or when system resources become low.
* @param level the memory-related event that is raised.
*/
override fun onTrimMemory(level: Int) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// Release memory related to UI elements, such as bitmap caches.
}
if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Release memory related to background processing, such as by
// closing a database connection.
}
}
}
Java
import android.content.ComponentCallbacks2;
// Other import statements.
public class MainActivity extends AppCompatActivity
implements ComponentCallbacks2 {
// Other activity code.
/**
* Release memory when the UI becomes hidden or when system resources become low.
* @param level the memory-related event that is raised.
*/
public void onTrimMemory(int level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// Release memory related to UI elements, such as bitmap caches.
}
if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Release memory related to background processing, such as by
// closing a database connection.
}
}
}
देखें कि आपको कितनी मेमोरी की ज़रूरत है
एक साथ कई प्रोसेस चलाने की अनुमति देने के लिए, Android हर ऐप्लिकेशन के लिए हीप साइज़ की एक तय सीमा सेट करता है. हीप साइज़ की सटीक सीमा, डिवाइस के हिसाब से अलग-अलग होती है. यह इस बात पर निर्भर करती है कि डिवाइस में कुल कितनी रैम उपलब्ध है. अगर आपका ऐप्लिकेशन, हीप
की क्षमता तक पहुंच जाता है और ज़्यादा मेमोरी ऐलोकेट करने की कोशिश करता है, तो सिस्टम
OutOfMemoryErrorदिखाता है.
मेमोरी खत्म होने से बचने के लिए, सिस्टम से क्वेरी करके यह पता लगाया जा सकता है कि मौजूदा डिवाइस पर हीप स्पेस कितना उपलब्ध है. getMemoryInfo() को कॉल करके, सिस्टम से इस
आंकड़े के लिए क्वेरी की जा सकती है. इससे
ActivityManager.MemoryInfo ऑब्जेक्ट मिलता है. इसमें
डिवाइस की मौजूदा मेमोरी की स्थिति के बारे में जानकारी होती है. जैसे, उपलब्ध मेमोरी, कुल मेमोरी, और
मेमोरी थ्रेशोल्ड. मेमोरी थ्रेशोल्ड, मेमोरी का वह लेवल होता है जिस पर सिस्टम, प्रोसेस को बंद करना शुरू कर देता है.
ActivityManager.MemoryInfo ऑब्जेक्ट, lowMemory भी दिखाता है. यह एक सामान्य बूलियन है. इससे पता चलता है कि डिवाइस
में मेमोरी कम है या नहीं.
यहां दिए गए कोड स्निपेट के उदाहरण में, आपके ऐप्लिकेशन में getMemoryInfo() मेथड का इस्तेमाल करने का तरीका बताया गया है.
Kotlin
fun doSomethingMemoryIntensive() {
// Before doing something that requires a lot of memory,
// check whether the device is in a low memory state.
if (!getAvailableMemory().lowMemory) {
// Do memory intensive work.
}
}
// Get a MemoryInfo object for the device's current memory status.
private fun getAvailableMemory(): ActivityManager.MemoryInfo {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
return ActivityManager.MemoryInfo().also { memoryInfo ->
activityManager.getMemoryInfo(memoryInfo)
}
}
Java
public void doSomethingMemoryIntensive() {
// Before doing something that requires a lot of memory,
// check whether the device is in a low memory state.
ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();
if (!memoryInfo.lowMemory) {
// Do memory intensive work.
}
}
// Get a MemoryInfo object for the device's current memory status.
private ActivityManager.MemoryInfo getAvailableMemory() {
ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
return memoryInfo;
}
कम मेमोरी की वजह से प्रोसेस बंद करने की निगरानी करना
उपयोगकर्ताओं को दिखने वाली, कम मेमोरी की वजह से प्रोसेस बंद करने (एलएमके) की गड़बड़ियां तब होती हैं, जब सिस्टम मेमोरी बहुत कम हो जाती है. मेमोरी कम होने पर,
lmkd (कम मेमोरी की वजह से प्रोसेस बंद करने वाला
डेमॉन), प्रोसेस को उनके oom_adj_score के आधार पर बंद कर देता है. कैश मेमोरी में सेव किए गए ऐप्लिकेशन या बिना यूज़र इंटरफ़ेस (यूआई) वाली सेवा (जैसे, कोई जॉब) चलाने वाले ऐप्लिकेशन के स्कोर सबसे ज़्यादा होते हैं. इसलिए, उन्हें सबसे पहले बंद किया जाता है. अगर मेमोरी बहुत कम रहती है, तो डेमॉन को oom_adj_score वाले प्रोसेस से मेमोरी वापस लेनी पड़ती है.
वह स्कोर, दिखने वाले ऐप्लिकेशन के लिए रिज़र्व होता है. इसलिए, उन्हें बंद करने पर, प्रोसेस तुरंत और बिना किसी सूचना के बंद हो जाती है. आखिरी उपयोगकर्ता को ऐसा लगता है कि ऐप्लिकेशन क्रैश हो गया है. अक्सर, यह लाइफ़साइकल की स्थिति को सेव करने के सामान्य तरीकों को बायपास कर देता है. इससे उपयोगकर्ता की प्रोग्रेस का डेटा खो जाता है.
Android Vitals में, फ़ोरग्राउंड प्रोसेस को बंद करने पर ज़्यादा फ़ोकस किया जाता है, क्योंकि ये मेमोरी के गलत तरीके से इस्तेमाल की वजह से होने वाली गड़बड़ियों की सटीक जानकारी देते हैं. अगर एलएमके रेट 1% से ज़्यादा है, तो इसका मतलब है कि तुरंत कार्रवाई करने की ज़रूरत है. हालांकि, कम रेट का मतलब यह नहीं है कि ऐप्लिकेशन सही तरीके से काम कर रहा है. हो सकता है कि यूज़र-पर्सीव्ड एलएमके रेट कम होने का मतलब यह हो कि एलएमके डेमॉन, बैकग्राउंड में चल रही प्रोसेस को बार-बार बंद कर रहा है. इससे "वॉर्म स्टार्ट" परफ़ॉर्मेंस और मल्टीटास्किंग की सुविधा खराब हो जाती है. इसलिए, हमारा सुझाव है कि एलएमके स्कोर कुछ भी हो, मेमोरी के सबसे सही तरीकों का पालन करें. इससे, लंबे समय तक डिवाइस की स्थिति और सुरक्षा को बेहतर बनाए रखने में मदद मिलती है.
मेमोरी से जुड़ी समस्याओं को ट्रैक करने के लिए, ProfilingManager का इस्तेमाल करना
Android प्लैटफ़ॉर्म
ProfilingManager, एक
बेहतर ऑब्ज़र्वेबिलिटी एपीआई उपलब्ध कराता है. इसकी मदद से, सेट किए गए ट्रिगर के आधार पर, प्रोडक्शन में उपयोगकर्ता का डेटा कैप्चर किया जा सकता है.
इससे, मेमोरी से जुड़ी उन समस्याओं की पहचान करने में मदद मिल सकती है जिन्हें फिर से नहीं बनाया जा सकता.
Android 17 में दो नए ट्रिगर जोड़े गए हैं. ये मेमोरी से जुड़ी समस्याओं का पता लगाने के लिए खास तौर पर काम के हैं:
TRIGGER_TYPE_OOMसे पता चलता है कि ऐप्लिकेशन नेOutOfMemoryErrorदिखाया है. यह तब ट्रिगर होता है, जब क्रैश होने के बाद ऐप्लिकेशन अगली बार शुरू होता है. ऐसा तब होता है, जब ऐप्लिकेशन, प्रोफ़ाइलिंग ट्रिगर के लिए रजिस्टर करता है.TRIGGER_TYPE_ANOMALYतब ट्रिगर होता है, जब सिस्टम को ऐप्लिकेशन के अनियमित व्यवहार का पता चलता है. अन्य चीज़ों के अलावा, यह मेमोरी के ज़्यादा इस्तेमाल की वजह से भी ट्रिगर हो सकता है. यह तब ट्रिगर होता है, जब ऐप्लिकेशन, मेमोरी का ज़्यादा इस्तेमाल करता है. साथ ही, यह सिस्टम के, गड़बड़ी वाली प्रोसेस को बंद करने के लिए कोई कार्रवाई करने से पहले ट्रिगर होता है. उदाहरण के लिए, अगर ऐप्लिकेशन Android 17 में तय की गई मेमोरी की सीमाओं से ज़्यादा मेमोरी का इस्तेमाल करता है, तो सिस्टम के ऐप्लिकेशन को बंद करने से पहले,TRIGGER_TYPE_ANOMALYट्रिगर होता है.
प्रोग्राम के ज़रिए ट्रिगर रजिस्टर करने
और उन्हें वापस पाने के लिए, ProfilingManager का इस्तेमाल करने के बारे में ज़्यादा जानकारी के लिए, ट्रिगर-आधारित
प्रोफ़ाइलिंग
का दस्तावेज़ देखें.
ऐप्लिकेशन-ड्राइविंग प्रोफ़ाइलिंग का इस्तेमाल करके, ट्रेस पॉइंट को मैन्युअल तरीके से भी तय किया जा सकता है. हमारा सुझाव है कि हीप डंप या हीप प्रोफ़ाइल को मैन्युअल तरीके से कैप्चर करने के लिए, ऐसा करें. खास तौर पर, उन जगहों पर जहां आपको लगता है कि मेमोरी लीक हो सकती है या मेमोरी का ज़्यादा इस्तेमाल हो सकता है.
मेमोरी का कम इस्तेमाल करने वाले कोड कंस्ट्रक्ट का इस्तेमाल करना
Android की कुछ सुविधाओं, Java क्लास, और कोड कंस्ट्रक्ट में, दूसरों के मुकाबले ज़्यादा मेमोरी का इस्तेमाल होता है. अपने कोड में ज़्यादा असरदार विकल्प चुनकर, यह तय किया जा सकता है कि आपका ऐप्लिकेशन कितनी मेमोरी का इस्तेमाल करे.
सेवाओं का कम इस्तेमाल करना
हमारा सुझाव है कि ज़रूरत न होने पर, सेवाओं को चालू न रखें. ज़रूरत न होने पर भी सेवाओं को चालू रखना, Android ऐप्लिकेशन की मेमोरी मैनेजमेंट से जुड़ी सबसे बड़ी गड़बड़ियों में से एक है. अगर आपके ऐप्लिकेशन को बैकग्राउंड में काम करने के लिए किसी सेवा की ज़रूरत है, तो उसे तब तक चालू न रखें, जब तक उसे कोई जॉब रन न करनी हो. सेवा का टास्क पूरा होने पर, उसे बंद कर दें. ऐसा न करने पर, मेमोरी लीक हो सकती है.
किसी सेवा को शुरू करने पर, सिस्टम उस सेवा की प्रोसेस को चालू रखता है. इस व्यवहार की वजह से, सेवा की प्रोसेस बहुत महंगी हो जाती है, क्योंकि किसी सेवा के लिए इस्तेमाल की गई रैम, अन्य प्रोसेस के लिए उपलब्ध नहीं होती. इससे, कैश मेमोरी में सेव की गई प्रोसेस की संख्या कम हो जाती है. सिस्टम, एलआरयू कैश में इन प्रोसेस को सेव रखता है. इससे ऐप्लिकेशन स्विच करना कम असरदार हो जाता है. मेमोरी कम होने पर, सिस्टम में थ्रैशिंग भी हो सकती है. ऐसा तब होता है, जब सिस्टम, फ़िलहाल चल रही सभी सेवाओं को होस्ट करने के लिए, ज़रूरी प्रोसेस को चालू नहीं रख पाता.
आम तौर पर, पर्सिस्टेंट सेवाओं का इस्तेमाल न करें, क्योंकि ये उपलब्ध मेमोरी पर लगातार दबाव डालती हैं. इसके बजाय, हमारा सुझाव है कि आप किसी अन्य तरीके का इस्तेमाल करें
. जैसे, WorkManager.
WorkManager का इस्तेमाल करके, बैकग्राउंड
प्रोसेस शेड्यूल करने के बारे में ज़्यादा जानकारी के लिए, पर्सिस्टेंट वर्क देखें.
ऑप्टिमाइज़ किए गए डेटा कंटेनर का इस्तेमाल करना
प्रोग्रामिंग लैंग्वेज से उपलब्ध कराई गई कुछ क्लास, मोबाइल डिवाइसों पर इस्तेमाल के लिए ऑप्टिमाइज़ नहीं की जाती हैं. उदाहरण के लिए, सामान्य
HashMap को लागू करने में, मेमोरी
का ज़्यादा इस्तेमाल हो सकता है, क्योंकि इसे हर मैपिंग के लिए अलग एंट्री ऑब्जेक्ट की ज़रूरत होती है.
Android फ़्रेमवर्क में, ऑप्टिमाइज़ किए गए कई डेटा कंटेनर शामिल हैं. जैसे,
SparseArray,
SparseBooleanArray, और
LongSparseArray. उदाहरण के लिए, SparseArray क्लास ज़्यादा असरदार होती हैं, क्योंकि ये सिस्टम को कुंजी और कभी-कभी वैल्यू को ऑटोबॉक्स करने से रोकती हैं. इससे हर एंट्री के लिए एक या दो और ऑब्जेक्ट बन जाते हैं.
ज़रूरत पड़ने पर, डेटा के छोटे स्ट्रक्चर के लिए, हमेशा रॉ ऐरे पर स्विच किया जा सकता है.
कोड ऐब्स्ट्रैक्शन का इस्तेमाल करते समय सावधानी बरतें
डेवलपर अक्सर ऐब्स्ट्रैक्शन का इस्तेमाल, प्रोग्रामिंग के एक अच्छे तरीके के तौर पर करते हैं, क्योंकि इससे कोड की फ़्लेक्सिबिलिटी और रखरखाव बेहतर हो सकता है. हालांकि, आम तौर पर ऐब्स्ट्रैक्शन के लिए, ज़्यादा कोड को एक्ज़ीक्यूट करने की ज़रूरत होती है. अपने ऐप्लिकेशन के कोड और संसाधन के फ़ुटप्रिंट को कम करना में बताया गया है कि कंपाइल किया गया बड़ा कोडबेस, आपके ऐप्लिकेशन के लिए ज़रूरी फ़िज़िकल रैम को सीधे तौर पर बढ़ाता है. अगर आपके ऐब्स्ट्रैक्शन से ज़्यादा फ़ायदा नहीं मिलता है, तो उनका इस्तेमाल न करें.
सीरियलाइज़ किए गए डेटा के लिए, लाइट प्रोटोबफ़ का इस्तेमाल करना
प्रोटोकॉल बफ़र (प्रोटोबफ़) Google की ओर से डिज़ाइन किया गया एक भाषा-न्यूट्रल, प्लैटफ़ॉर्म-न्यूट्रल, एक्सटेंसिबल मैकेनिज़्म है. इसका इस्तेमाल, स्ट्रक्चर्ड डेटा को सीरियलाइज़ करने के लिए किया जाता है. यह एक्सएमएल की तरह होता है, लेकिन छोटा, तेज़, और आसान होता है. अपने डेटा के लिए प्रोटोबफ़ का इस्तेमाल करते समय, क्लाइंट-साइड कोड में हमेशा लाइट प्रोटोबफ़ का इस्तेमाल करें. सामान्य प्रोटोबफ़, बहुत ज़्यादा वर्बोस कोड जनरेट करते हैं. इससे रैम में आपके ऐप्लिकेशन का कोड फ़ुटप्रिंट बढ़ जाता है (अपने ऐप्लिकेशन के कोड और संसाधन के फ़ुटप्रिंट को कम करना देखें). साथ ही, एपीके का साइज़ भी बढ़ जाता है.
ज़्यादा जानकारी के लिए, प्रोटोबफ़ readme देखें.
मेमोरी लीक के बारे में सावधानी बरतें
रेफ़रंस को सही तरीके से मैनेज न करने पर, मेमोरी लीक हो सकती है. इसमें ऑब्जेक्ट, अपने काम के लाइफ़स्पैन से ज़्यादा समय तक सेव रहते हैं. इससे गार्बेज कलेक्टर, लीक हुए ऑब्जेक्ट की मेमोरी को वापस नहीं ले पाता. मेमोरी लीक से बचने के लिए, लाइफ़साइकल-अवेयर डिज़ाइन लागू करें.
मेमोरी चर्न से बचना
गार्बेज कलेक्शन इवेंट से, आपके ऐप्लिकेशन की परफ़ॉर्मेंस पर कोई असर नहीं पड़ता. हालांकि, कम समय में कई गार्बेज कलेक्शन इवेंट होने से, बैटरी तेज़ी से खत्म हो सकती है. साथ ही, फ़्रेम सेट अप करने में लगने वाला समय भी थोड़ा बढ़ सकता है. ऐसा इसलिए, क्योंकि गार्बेज कलेक्टर और ऐप्लिकेशन थ्रेड के बीच ज़रूरी इंटरैक्शन होते हैं. सिस्टम, गार्बेज कलेक्शन में जितना ज़्यादा समय बिताता है, बैटरी उतनी ही तेज़ी से खत्म होती है.
अक्सर, मेमोरी चर्न की वजह से, कई गार्बेज कलेक्शन इवेंट हो सकते हैं. मेमोरी चर्न, किसी तय समय में ऐलोकेट किए गए अस्थायी ऑब्जेक्ट की संख्या को दिखाता है.
उदाहरण के लिए, for लूप में कई अस्थायी ऑब्जेक्ट ऐलोकेट किए जा सकते हैं.
इसके अलावा, किसी व्यू के
onDraw()
फ़ंक्शन में, नए Paint या
Bitmap ऑब्जेक्ट बनाए जा सकते हैं. दोनों ही मामलों में, ऐप्लिकेशन बहुत कम समय में, बड़ी संख्या में ऑब्जेक्ट बनाता है. इससे, यंग जनरेशन में उपलब्ध सभी मेमोरी तेज़ी से खत्म हो सकती है. इससे गार्बेज कलेक्शन इवेंट हो सकता है.
मेमोरी चर्न की समस्या को ठीक करने से पहले, मेमोरी प्रोफ़ाइलर का इस्तेमाल करके, अपने कोड में उन जगहों का पता लगाएं जहां मेमोरी चर्न ज़्यादा है.
अपने कोड में समस्या वाली जगहों की पहचान करने के बाद, परफ़ॉर्मेंस के लिए ज़रूरी जगहों में ऐलोकेशन की संख्या कम करने की कोशिश करें. इनर लूप से चीज़ें हटाएं या उन्हें फ़ैक्ट्री-आधारित ऐलोकेशन स्ट्रक्चर में ले जाएं.
यह भी देखें कि ऑब्जेक्ट पूल से, इस्तेमाल के मामले में फ़ायदा मिलता है या नहीं. ऑब्जेक्ट पूल की मदद से, ऑब्जेक्ट इंस्टेंस को हटाने के बजाय, ज़रूरत न होने पर उसे पूल में रिलीज़ किया जा सकता है. अगली बार, उस टाइप के ऑब्जेक्ट इंस्टेंस की ज़रूरत होने पर, उसे ऐलोकेट करने के बजाय, पूल से हासिल किया जा सकता है.
यह तय करने के लिए कि किसी दी गई स्थिति में ऑब्जेक्ट पूल सही है या नहीं, परफ़ॉर्मेंस का अच्छी तरह से आकलन करें. ऐसे मामले भी हो सकते हैं जिनमें ऑब्जेक्ट पूल से परफ़ॉर्मेंस खराब हो सकती है. पूल, ऐलोकेशन से बचते हैं, लेकिन इनसे अन्य ओवरहेड बढ़ जाते हैं. उदाहरण के लिए, पूल को बनाए रखने में आम तौर पर सिंक्रनाइज़ेशन शामिल होता है. इससे काफ़ी ओवरहेड होता है. इसके अलावा, रिलीज़ के दौरान मेमोरी लीक से बचने के लिए, पूल किए गए ऑब्जेक्ट इंस्टेंस को साफ़ करने और फिर उसे हासिल करने के दौरान इनिशियलाइज़ करने से, ओवरहेड हो सकता है.
पूल में ज़रूरत से ज़्यादा ऑब्जेक्ट इंस्टेंस सेव रखने से, गार्बेज कलेक्शन पर भी दबाव पड़ता है. ऑब्जेक्ट पूल से, गार्बेज कलेक्शन के लिए किए जाने वाले कॉल की संख्या कम हो जाती है. हालांकि, इससे हर कॉल के लिए ज़रूरी काम की मात्रा बढ़ जाती है, क्योंकि यह लाइव (पहुंचे जा सकने वाले) बाइट की संख्या के अनुपात में होती है.