अपने ऐप्लिकेशन की मेमोरी मैनेज करना

इस पेज पर, आपके ऐप्लिकेशन में मेमोरी के इस्तेमाल को कम करने का तरीका बताया गया है. Android ऑपरेटिंग सिस्टम, मेमोरी को कैसे मैनेज करता है, इस बारे में जानने के लिए मेमोरी मैनेजमेंट की खास जानकारी देखें.

रैंडम-ऐक्सेस मेमोरी (रैम), सॉफ़्टवेयर डेवलपमेंट के किसी भी एनवायरमेंट के लिए एक अहम संसाधन है. यह मोबाइल ऑपरेटिंग सिस्टम के लिए और भी ज़्यादा अहम है, क्योंकि इसमें फ़िज़िकल मेमोरी अक्सर सीमित होती है. Android Runtime (एआरटी) और Dalvik वर्चुअल मशीन, दोनों ही नियमित तौर पर गार्बेज कलेक्शन की प्रोसेस पूरी करती हैं. हालांकि, इसका मतलब यह नहीं है कि आपको इस बात पर ध्यान देने की ज़रूरत नहीं है कि आपका ऐप्लिकेशन कब और कहां मेमोरी को असाइन और रिलीज़ करता है. आपको अब भी मेमोरी लीक से बचना होगा. आम तौर पर, ऐसा स्टैटिक मेंबर वैरिएबल में ऑब्जेक्ट रेफ़रंस बनाए रखने की वजह से होता है. साथ ही, लाइफ़साइकल कॉलबैक के हिसाब से, सही समय पर Reference ऑब्जेक्ट रिलीज़ करने होंगे.

अपने ऐप्लिकेशन के कोड और संसाधन के फ़ुटप्रिंट को कम करें

आपके कोड में मौजूद कुछ संसाधन और लाइब्रेरी, आपकी जानकारी के बिना मेमोरी का इस्तेमाल कर सकती हैं. आपके ऐप्लिकेशन का कुल साइज़, ऐप्लिकेशन की मेमोरी पर असर डाल सकता है. इसमें तीसरे पक्ष की लाइब्रेरी या एम्बेड किए गए संसाधन शामिल हैं. अपने कोड से ऐसे कॉम्पोनेंट, संसाधन, और लाइब्रेरी हटाकर अपने ऐप्लिकेशन की मेमोरी खपत को बेहतर बनाया जा सकता है जो ज़रूरत से ज़्यादा, गैर-ज़रूरी या बहुत बड़े हैं.

R8 को चालू करके, ऐप्लिकेशन का कुल साइज़ कम करें

आपका कंपाइल किया गया ऐप्लिकेशन कोड, रनटाइम मेमोरी के फ़ुटप्रिंट का एक ऐक्टिव हिस्सा होता है. हर क्लास, मेथड, लाइब्रेरी डिपेंडेंसी, और स्ट्रिंग कॉन्स्टेंट को चलाने के लिए, RAM में लोड करना ज़रूरी है. आपका कंपाइल किया गया कोडबेस जितना बड़ा होगा, आपके ऐप्लिकेशन को उतनी ही ज़्यादा फ़िज़िकल RAM की ज़रूरत होगी.

R8 का इस्तेमाल करके, अपने ऐप्लिकेशन के मेमोरी फ़ुटप्रिंट को कम किया जा सकता है. R8 को आम तौर पर APK का साइज़ कम करने के लिए जाना जाता है. हालांकि, इसका सीधा और सकारात्मक असर रनटाइम मेमोरी (RAM) पर भी पड़ता है. R8, आपके ऐप्लिकेशन के बाइट कोड का विश्लेषण करता है, ताकि डेड कोड को हटाया जा सके, एक जैसी क्लास को मर्ज किया जा सके, इनलाइन मेथड को मर्ज किया जा सके, और आइडेंटिफ़ायर को छोटा किया जा सके. यह APK से कम कंपाइल किया गया बाइटकोड, रैम में लोड करता है. इससे ऐप्लिकेशन के मेमोरी फ़ुटप्रिंट में कमी आती है. इसके अलावा, क्लास, मेथड, और फ़ील्ड के नामों को छोटे आइडेंटिफ़ायर में छोटा करने से, सीधे तौर पर रैम का इस्तेमाल कम हो जाता है. क्लास मर्जिंग और एक्सटेंसिव मेथड इनलाइनिंग जैसे ऑप्टिमाइज़ेशन, रनटाइम लुकअप और ऐलोकेशन पैटर्न को भी बदलते हैं. इससे हीप और स्टैक मेमोरी को ऑप्टिमाइज़ किया जा सकता है.

डेटा को सुरक्षित रखने से जुड़े नियमों के बारे में जानकारी

कीप नियम, कॉन्फ़िगरेशन से जुड़े निर्देश होते हैं. इनसे R8 को यह पता चलता है कि ऑप्टिमाइज़ेशन के दौरान, आपके कोड के किन हिस्सों को सुरक्षित रखना है. इससे R8, आपके ऐप्लिकेशन के लिए ज़रूरी कोड को हटाने या छोटा करने से बचता है. ज़्यादा जानकारी के लिए, डेटा सुरक्षित रखने के नियमों के बारे में खास जानकारी देखें.

गलत तरीके से लिखे गए कीप नियमों की वजह से, R8 आपके कोडबेस के बड़े हिस्से को ऑप्टिमाइज़ नहीं कर पाता. डेटा को सुरक्षित रखने के लिए, बहुत ज़्यादा सामान्य नियम न बनाएं. इसके बजाय, इन सबसे सही तरीकों का पालन करें:

  • ग्लोबल नियमों का उल्लंघन करने से बचें:
    • -dontoptimize: इससे पूरे ऐप्लिकेशन के लिए ऑप्टिमाइज़ेशन की सुविधा बंद हो जाती है. इससे एक्ज़ीक्यूटेबल फ़ाइलें बड़ी हो जाती हैं और उनकी स्पीड कम हो जाती है.
    • -dontshrink: इससे इस्तेमाल न होने वाले कोड और संसाधनों को हटाने से रोका जाता है.
    • -dontobfuscate: इससे नाम को छोटा होने से रोका जाता है. साथ ही, मेमोरी की बचत करने का अहम फ़ायदा मिलता है. यह फ़ायदा खास तौर पर बड़े ऐप्लिकेशन में मिलता है.
  • पूरे पैकेज के लिए वाइल्डकार्ड का इस्तेमाल न करें: -keep class com.example.package.** { *; } जैसे सामान्य नियमों की वजह से, R8 को उस पैकेज में मौजूद हर क्लास, फ़ील्ड, और तरीके को बनाए रखना पड़ता है. इससे R8, उस पैकेज में मौजूद कोड को हटाने, ऑप्टिमाइज़ करने या छोटा करने की सुविधा का इस्तेमाल नहीं कर पाता.

  • R8 की डिफ़ॉल्ट कॉन्फ़िगरेशन फ़ाइल का इस्तेमाल करें: हमेशा proguard-android-optimize.txt का इस्तेमाल करें.

डेटा को सुरक्षित रखने के नियम लिखने के बारे में ज़्यादा जानने के लिए, डेटा को सुरक्षित रखने के नियमों की खास जानकारी देखें. इस्तेमाल किए जाने वाले और इस्तेमाल न किए जाने वाले खास पैटर्न के बारे में जानने के लिए, कीप के नियमों से जुड़े सबसे सही तरीके लेख पढ़ें.

R8 कॉन्फ़िगरेशन ऐनलिसिस टूल, आपके R8 कॉन्फ़िगरेशन के बारे में अहम जानकारी देता है. साथ ही, यह बताता है कि हर कीप नियम का आपके ऐप्लिकेशन पर क्या असर पड़ता है. ऑप्टिमाइज़ेशन को ब्लॉक करने वाले नियमों की पहचान करने के तरीके के बारे में ज़्यादा जानने के लिए, R8 कॉन्फ़िगरेशन ऐनलिसिस टूल देखें.

एक्सटर्नल लाइब्रेरी का इस्तेमाल करते समय सावधानी बरतें

बाहरी लाइब्रेरी का कोड अक्सर मोबाइल एनवायरमेंट के लिए नहीं लिखा जाता है. इसलिए, यह मोबाइल क्लाइंट पर काम करने के लिए सही नहीं होता. किसी बाहरी लाइब्रेरी का इस्तेमाल करते समय, आपको उसे फ़ोन या टैबलेट के लिए ऑप्टिमाइज़ करना पड़ सकता है. इस काम की योजना पहले से बना लें. साथ ही, लाइब्रेरी का इस्तेमाल करने से पहले, कोड साइज़ और रैम फ़ुटप्रिंट के हिसाब से उसका विश्लेषण करें.

मोबाइल के लिए ऑप्टिमाइज़ की गई कुछ लाइब्रेरी भी अलग-अलग तरीके से लागू होने की वजह से समस्याएं पैदा कर सकती हैं. उदाहरण के लिए, ऐसा हो सकता है कि एक लाइब्रेरी लाइट प्रोटोबफ़ का इस्तेमाल करती हो, जबकि दूसरी माइक्रो प्रोटोबफ़ का इस्तेमाल करती हो. इससे आपके ऐप्लिकेशन में दो अलग-अलग प्रोटोबफ़ लागू हो जाते हैं. ऐसा लॉगिंग, आंकड़ों, इमेज लोड करने वाले फ़्रेमवर्क, कैश मेमोरी, और कई अन्य चीज़ों के अलग-अलग तरीकों से लागू होने पर हो सकता है.

R8 का इस्तेमाल करके अपने ऐप्लिकेशन को ऑप्टिमाइज़ करने से, डिपेंडेंसी से इस्तेमाल न किए गए कोड को हटाया जा सकता है. हालांकि, इसकी असरदारिता अक्सर लाइब्रेरी के इंटरनल कॉन्फ़िगरेशन से सीमित होती है. उदाहरण के लिए, लाइब्रेरी में रिफ़्लेक्शन का इस्तेमाल करने या नियमों को लागू रखने से, R8 को कोड छोटा करने से रोका जा सकता है. इससे मेमोरी का ज़्यादा इस्तेमाल हो सकता है. लाइब्रेरी को बेहतर तरीके से चुनने की रणनीतियों के लिए, लाइब्रेरी को सोच-समझकर चुनें लेख पढ़ें.

शेयर की गई लाइब्रेरी का इस्तेमाल सिर्फ़ एक या दो सुविधाओं के लिए न करें. ऐसे कोड और ओवरहेड को शामिल न करें जिनका इस्तेमाल नहीं किया जाता. किसी लाइब्रेरी का इस्तेमाल करने से पहले, यह देख लें कि वह आपकी ज़रूरतों के हिसाब से सही है या नहीं. इसके अलावा, आपके पास खुद का लागू करने का तरीका बनाने का विकल्प भी है.

डिपेंडेंसी इंजेक्शन के लिए, Hilt या Dagger 2 का इस्तेमाल करना

डिपेंडेंसी इंजेक्शन फ़्रेमवर्क, आपके लिखे गए कोड को आसान बना सकते हैं. साथ ही, एक ऐसा अडैप्टिव एनवायरमेंट उपलब्ध करा सकते हैं जो टेस्टिंग और कॉन्फ़िगरेशन में किए जाने वाले अन्य बदलावों के लिए फ़ायदेमंद हो.

अगर आपको अपने ऐप्लिकेशन में डिपेंडेंसी इंजेक्शन फ़्रेमवर्क का इस्तेमाल करना है, तो Hilt या Dagger का इस्तेमाल करें. Hilt, Android के लिए एक डिपेंडेंसी इंजेक्शन लाइब्रेरी है. यह Dagger के ऊपर काम करती है. Dagger, आपके ऐप्लिकेशन के कोड को स्कैन करने के लिए रिफ़्लेक्शन का इस्तेमाल नहीं करता. Android ऐप्लिकेशन में, Dagger के स्टैटिक कंपाइल-टाइम इंप्लीमेंटेशन का इस्तेमाल किया जा सकता है. इससे रनटाइम में होने वाले खर्च या मेमोरी के इस्तेमाल पर कोई असर नहीं पड़ता.

डिपेंडेंसी इंजेक्शन के अन्य फ़्रेमवर्क, रिफ़्लेक्शन का इस्तेमाल करते हैं. ये एनोटेशन के लिए आपके कोड को स्कैन करके, प्रोसेस शुरू करते हैं. इस प्रोसेस में, सीपीयू साइकल और रैम की काफ़ी ज़्यादा ज़रूरत पड़ सकती है. साथ ही, इससे ऐप्लिकेशन लॉन्च होने में काफ़ी समय लग सकता है.

डिपेंडेंसी इंजेक्शन का इस्तेमाल करते समय, मेमोरी लीक से बचने के लिए सावधानी बरतें. इसके लिए, यह पक्का करें कि ऑब्जेक्ट को सही तरीके से स्कोप किया गया हो. ज़रूरत से ज़्यादा समय तक ऑब्जेक्ट बनाए रखने से मेमोरी लीक हो सकती है. ऐसा तब होता है, जब उन्हें गलत लाइफ़साइकल से बाइंड किया जाता है.

इमेज लोड करने के लिए, मकसद तय करें

ग्राफ़िक बिटमैप, आम तौर पर आपके ऐप्लिकेशन की मेमोरी में मौजूद सबसे बड़े ऑब्जेक्ट होते हैं. अगर कंप्रेस की गई फ़ाइलों (जैसे, JPEG) का इस्तेमाल किया जा रहा है, तो भी फ़ाइल को स्क्रीन पर दिखाने के लिए, उसे बिना कंप्रेस किए गए बिटमैप में बदलना होगा. कंप्रेस की गई छोटी इमेज फ़ाइल को बहुत बड़े बिटमैप में बदला जा सकता है.

उदाहरण के लिए, ज़्यादातर बिटमैप ARGB_8888 कॉन्फ़िगरेशन का इस्तेमाल करते हैं. इसका मतलब है कि हर पिक्सल के लिए 4 बाइट मेमोरी की ज़रूरत होती है. इसमें लाल, हरे, नीले, और ऐल्फ़ा (पारदर्शिता) के लिए एक-एक बाइट मेमोरी की ज़रूरत होती है. अगर आपके पास 100 केबी का JPEG है और उसे 1000×1000 पिक्सल व्यू में दिखाया जाता है, तो बिटमैप को उन 10 लाख पिक्सल में से हर पिक्सल के लिए 4 बाइट की ज़रूरत होगी. इससे 4 एमबी मेमोरी का इस्तेमाल होगा.

इमेज का इस्तेमाल ऑप्टिमाइज़ करने के लिए, कई तरीके अपनाए जा सकते हैं. उदाहरण के लिए, इमेज लोड करने वाली लाइब्रेरी का इस्तेमाल करने से, जब मेमोरी की ज़रूरत न हो, तब उसे रिलीज़ करने में मदद मिल सकती है. इमेज को बेहतर तरीके से मैनेज करने के बारे में जानकारी के लिए, बिटमैप इमेज को ऑप्टिमाइज़ करना लेख पढ़ें.

उपलब्ध मेमोरी और मेमोरी के इस्तेमाल की निगरानी करना

ऐप्लिकेशन की मेमोरी के इस्तेमाल से जुड़ी समस्याओं को ठीक करने से पहले, आपको उनका पता लगाना होगा. Android Studio में मौजूद मेमोरी प्रोफ़ाइलर, मेमोरी से जुड़ी समस्याओं का पता लगाने और उन्हें ठीक करने में आपकी मदद करता है. इसके लिए, वह ये काम करता है:

मेमोरी प्रोफ़ाइलर, LeakCanary लीक डिटेक्शन लाइब्रेरी के साथ भी इंटिग्रेट होता है. LeakCanary का इस्तेमाल करके, मेमोरी लीक के विश्लेषण को टेस्ट डिवाइस से डेवलपमेंट मशीन पर ले जाया जा सकता है. इससे आपके वर्कफ़्लो की स्पीड काफ़ी बढ़ सकती है. ज़्यादा जानकारी के लिए, Android Studio में हुए अपडेट की जानकारी देने वाला दस्तावेज़ देखें.

यहां कुछ ऐसे टूल दिए गए हैं जिनका इस्तेमाल करके, मेमोरी से जुड़ी समस्याओं का पता लगाया जा सकता है. ये समस्याएं, प्रोडक्शन ऐप्लिकेशन इस्तेमाल करने वाले लोगों के डेटा के आधार पर होती हैं:

इवेंट के जवाब में मेमोरी रिलीज़ करना

अगर ज़रूरी हो, तो Android आपके ऐप्लिकेशन से मेमोरी वापस ले सकता है या उसे पूरी तरह से बंद कर सकता है. ऐसा इसलिए किया जाता है, ताकि ज़रूरी टास्क के लिए मेमोरी खाली की जा सके. इस बारे में मेमोरी मैनेजमेंट की खास जानकारी में बताया गया है. सिस्टम मेमोरी को बैलेंस करने और सिस्टम को आपके ऐप्लिकेशन की प्रोसेस को रोकने से बचाने के लिए, अपनी Activity क्लास में ComponentCallbacks2 इंटरफ़ेस लागू किया जा सकता है. उपलब्ध कराया गया 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 0 है. यह स्कोर सिर्फ़ उन ऐप्लिकेशन के लिए रिज़र्व होता है जो दिखते हैं. इसलिए, ऐसे ऐप्लिकेशन को बंद करने पर, उन्हें तुरंत बंद कर दिया जाता है. एंड यूज़र को ऐसा लगता है कि ऐप्लिकेशन क्रैश हो गया है. ऐसा अक्सर, लाइफ़साइकल की स्थिति को सेव करने वाले स्टैंडर्ड मेकेनिज़्म को बायपास करने की वजह से होता है. इससे उपयोगकर्ता की प्रोग्रेस का डेटा मिट जाता है.

Android Vitals में, फ़ोरग्राउंड प्रोसेस के बंद होने की समस्या पर खास तौर पर ध्यान दिया जाता है. इसकी वजह यह है कि इससे मेमोरी को सही तरीके से मैनेज न करने की समस्या का पता चलता है. एलएमके की दर 1% से ज़्यादा होने का मतलब है कि तुरंत कार्रवाई करने की ज़रूरत है. हालांकि, एलएमके की दर कम होने का मतलब यह नहीं है कि आपका खाता सुरक्षित है. उपयोगकर्ता को दिखने वाले एलएमके रेट के कम होने का मतलब यह हो सकता है कि एलएमके डेमॉन, बैकग्राउंड में चल रही प्रोसेस को बार-बार बंद कर रहा है. इससे "वार्म स्टार्ट" की परफ़ॉर्मेंस और मल्टीटास्किंग की सुविधा पर असर पड़ता है. इसलिए, हमारा सुझाव है कि आप मेमोरी से जुड़े सबसे सही तरीकों का पालन करें. इससे कोई फ़र्क़ नहीं पड़ता कि आपका मौजूदा एलएमके स्कोर क्या है. इससे यह पक्का किया जा सकेगा कि आपका डिवाइस लंबे समय तक ठीक से काम करे और डिवाइस की स्थिति बेहतर बनी रहे.

मेमोरी से जुड़ी समस्याओं को ट्रैक करने के लिए, ProfilingManager का इस्तेमाल करना

Android प्लैटफ़ॉर्म, ProfilingManager उपलब्ध कराता है. यह एक बेहतर ऑब्ज़र्वेबिलिटी एपीआई है. इसकी मदद से, प्रोडक्शन के आधार पर उपयोगकर्ता का डेटा कैप्चर किया जा सकता है. यह डेटा, आपके सेट किए गए ट्रिगर के आधार पर कैप्चर किया जाता है. ऐसा करने से, आपको मेमोरी से जुड़ी उन समस्याओं का पता लगाने में मदद मिल सकती है जिन्हें दोहराना मुश्किल होता है.

Android 17 के साथ दो नए ट्रिगर जोड़े गए हैं. ये खास तौर पर मेमोरी से जुड़ी समस्याओं का पता लगाने के लिए उपयोगी हैं:

  • TRIGGER_TYPE_OOM इससे पता चलता है कि ऐप्लिकेशन ने OutOfMemoryError थ्रो किया है. यह क्रैश होने के बाद ऐप्लिकेशन के अगली बार शुरू होने पर ट्रिगर होता है. ऐसा तब होता है, जब ऐप्लिकेशन, प्रोफ़ाइलिंग ट्रिगर के लिए रजिस्टर करता है.
  • TRIGGER_TYPE_ANOMALY यह तब ट्रिगर होता है, जब सिस्टम को ऐप्लिकेशन के काम करने के तरीके में कोई गड़बड़ी मिलती है. यह गड़बड़ी, अन्य वजहों के साथ-साथ मेमोरी का बहुत ज़्यादा इस्तेमाल करने की वजह से भी हो सकती है. यह सूचना तब दिखती है, जब ऐप्लिकेशन बहुत ज़्यादा मेमोरी का इस्तेमाल कर रहा हो. साथ ही, यह सूचना तब दिखती है, जब सिस्टम, मेमोरी का ज़्यादा इस्तेमाल करने वाले ऐप्लिकेशन को बंद करने के लिए कोई कार्रवाई करने से पहले दिखती है. उदाहरण के लिए, अगर ऐप्लिकेशन Android 17 में तय की गई मेमोरी की सीमाओं से ज़्यादा मेमोरी इस्तेमाल करता है, तो सिस्टम के ऐप्लिकेशन को बंद करने से पहले TRIGGER_TYPE_ANOMALY ट्रिगर हो जाता है.

प्रोग्राम के हिसाब से ट्रिगर रजिस्टर करने और उन्हें वापस पाने के लिए, ProfilingManager का इस्तेमाल करने के बारे में ज़्यादा जानकारी के लिए, ट्रिगर पर आधारित प्रोफ़ाइलिंग का दस्तावेज़ देखें. Firebase Crashlytics, TRIGGER_TYPE_OOM और TRIGGER_TYPE_ANOMALY, दोनों ट्रिगर के साथ भी काम करता है. साथ ही, यह उन्हें अन्य डाइग्नोस्टिक मेटाडेटा से जोड़ता है.

ट्रेस के शुरू और खत्म होने के पॉइंट को मैन्युअल तरीके से तय करने के लिए, ऐप्लिकेशन के हिसाब से प्रोफ़ाइलिंग का इस्तेमाल भी किया जा सकता है. हमारा सुझाव है कि आप ऐसा करें, ताकि उन जगहों पर हीप डंप या हीप प्रोफ़ाइलें मैन्युअल तरीके से कैप्चर की जा सकें जहां आपको लगता है कि मेमोरी लीक हो सकती है या मेमोरी का बहुत ज़्यादा इस्तेमाल हो सकता है.

प्रोसेस की स्थिति और मेमोरी को ट्रैक करना

मेमोरी कॉलबैक और इवेंट के अलावा, अपने ऐप्लिकेशन की प्रोसेस की स्थिति और प्राथमिकता के बारे में साफ़ तौर पर क्वेरी की जा सकती है. यह खास तौर पर यह समझने के लिए मददगार है कि ऑपरेटिंग सिस्टम, आपके ऐप्लिकेशन की मौजूदा स्थिति को कैसे क्लासिफ़ाई करता है. उदाहरण के लिए, आपका ऐप्लिकेशन फ़ोरग्राउंड में है, उपयोगकर्ता को दिख रहा है या कैश मेमोरी में सेव है. साथ ही, यह भी पता चलता है कि क्या ऐप्लिकेशन को लो-मेमोरी किलर से बंद किए जाने का खतरा है.

लगातार मॉनिटर करने के लिए, Python डैशबोर्ड की मदद से

अपने ऐप्लिकेशन को चलाते समय, Android Memory Monitor Python स्क्रिप्ट का इस्तेमाल करें. इससे आपको अपने ऐप्लिकेशन की प्रोसेस और मेमोरी फ़ुटप्रिंट की लगातार निगरानी करने में मदद मिलेगी. यह एक कमांड-लाइन टूल है. यह adb कमांड का इस्तेमाल करके, टर्मिनल और वेब डैशबोर्ड दिखाता है. इससे आपको अपने ऐप्लिकेशन की प्रोसेस की स्थिति और मेमोरी के इस्तेमाल के बारे में समझने में मदद मिलती है. डैशबोर्ड लॉन्च करने के लिए, अपने ऐप्लिकेशन के पैकेज के नाम के साथ स्क्रिप्ट चलाएं. उदाहरण के लिए:

python3 android_mem_monitor.py your.package.name

डैशबोर्ड की सुविधाओं और उनके इस्तेमाल के बारे में ज़्यादा जानकारी के लिए, कृपया README फ़ाइल या स्क्रिप्ट में मौजूद इनलाइन टिप्पणियां देखें.

रनटाइम के दौरान ActivityManager

रनटाइम के दौरान चल रही प्रोसेस के बारे में जानकारी पाने के लिए, ActivityManager.getRunningAppProcesses() से क्वेरी की जा सकती है. इससे ऑब्जेक्ट की एक सूची मिलती है. इसमें आपके ऐप्लिकेशन की हर प्रोसेस के बारे में बुनियादी जानकारी होती है. importance फ़ील्ड से पता चलता है कि सिस्टम, प्रोसेस को कितनी अहमियत देता है. साथ ही, यह इन कॉन्स्टेंट से मैप होता है:

  • IMPORTANCE_FOREGROUND: प्रोसेस, फ़ोरग्राउंड यूज़र इंटरफ़ेस (यूआई) में चल रही है.
  • IMPORTANCE_FOREGROUND_SERVICE: प्रोसेस, फ़ोरग्राउंड सेवा (जैसे कि बैकग्राउंड में चल रहा म्यूज़िक या बड़े डाउनलोड) चला रही है. यह ऐसी सेवा का उदाहरण है जिसे उपयोगकर्ता देख सकता है.
  • IMPORTANCE_CACHED: इस प्रोसेस में कैश मेमोरी में सेव किया गया कोड शामिल है. इस प्रोसेस को फ़्रीज़ कर दिया जाता है और इसकी इस्तेमाल नहीं की गई मेमोरी को Android वापस ले लेता है. इस स्थिति में, ओएस MemoryLimiter, ऐप्लिकेशन के लिए तय की गई मेमोरी थ्रेशोल्ड से ज़्यादा मेमोरी इस्तेमाल करने पर, ऐप्लिकेशन को बंद नहीं करेगा. हालांकि, अगर सिस्टम की मेमोरी कम हो जाती है, तो ऐप्लिकेशन को लो-मेमोरी किलर के लिए चुना जाता है.

adb की मदद से डीबग करने के लिए

मेमोरी के इस्तेमाल और प्रोसेस के लाइफ़साइकल को डीबग करते समय, adb कमांड का इस्तेमाल किया जा सकता है. इससे आपको अपने ऐप्लिकेशन के OOM स्कोर और मेमोरी फ़ुटप्रिंट की जांच रीयल-टाइम में करने में मदद मिलती है. अपने ऐप्लिकेशन की प्रोसेस और मेमोरी की स्थितियों को मॉनिटर करने के लिए, इन निर्देशों का इस्तेमाल किया जा सकता है:

  • चालू प्रोसेस के आईडी (पीआईडी) ढूंढें: सबसे पहले, उन सभी प्रोसेस का पता लगाएं जिन्हें आपका ऐप्लिकेशन फ़िलहाल चला रहा है. इसके लिए, अपने पैकेज के नाम के हिसाब से फ़िल्टर की गई ps कमांड का इस्तेमाल करें.
    shell adb shell ps -A | grep your.package.name
  • मौजूदा ओओएम स्कोर देखें: प्रोसेस फ़ाइल सिस्टम से, किसी प्रोसेस का ओओएम स्कोर पढ़ने के लिए, प्रोसेस के पीआईडी का इस्तेमाल करें. आम तौर पर, 0 से 199 का स्कोर, फ़ोरग्राउंड में चल रहे ऐप्लिकेशन को दिखाता है. 200 से 249 का स्कोर, ऐसी सेवा को दिखाता है जिसे देखा जा सकता है. 250 से 899 का स्कोर, बैकग्राउंड में चलने वाली सेवा के लिए होता है. वहीं, 900 से ज़्यादा के स्कोर का मतलब है कि प्रोसेस को कैश मेमोरी में सेव किया गया है और इसे बंद किया जा सकता है.
    shell adb shell cat /proc/pid/oom_score_adj
  • ओओएम स्कोर पर लगातार नज़र रखें: किसी ऐप्लिकेशन के ओओएम स्कोर में होने वाले बदलावों पर लगातार नज़र रखने के लिए, watch-uids कमांड का इस्तेमाल किया जा सकता है.
    ओओएम स्कोर, ऐप्लिकेशन की सभी प्रोसेस में सबसे कम ओओएम स्कोर होता है.shell adb shell am watch-uids
  • मेमोरी फ़ुटप्रिंट का आकलन करना: किसी प्रोसेस के मुख्य मेमोरी फ़ुटप्रिंट, जैसे कि ऐनोनिमस आरएसएस और स्वैप मेमोरी के इस्तेमाल की जानकारी तुरंत पाने के लिए, linux प्रोसेस फ़ाइल सिस्टम से सीधे तौर पर उसकी स्थिति पढ़ें.
    shell adb shell cat /proc/pid/status | grep -E "VmRSS|RssAnon|VmSwap"
    मेमोरी के बंटवारे के बारे में ज़्यादा जानकारी और पूरी जानकारी पाने के लिए, dumpsys meminfo कमांड का इस्तेमाल करें:
    shell adb shell dumpsys meminfo pid
  • चालू कॉम्पोनेंट की जांच करें: अगर आपकी प्रोसेस को उम्मीद से ज़्यादा प्राथमिकता मिल रही है (जैसे कि कैश मेमोरी में सेव होने के बजाय, ओओएम स्कोर 200 पर बनी हुई है), तो यह देखा जा सकता है कि प्रोसेस क्या चला रही है. dumpsys activity processes कमांड से, उन खास ऐक्टिविटी, सेवाओं, प्रोवाइडर, और रिसीवर के बारे में पता चलता है जो प्रोसेस को चालू रखते हैं.
    shell adb shell dumpsys activity processes your.package.name

मेमोरी का कम इस्तेमाल करने वाले कोड कंस्ट्रक्ट का इस्तेमाल करें

Android की कुछ सुविधाओं, Java क्लास, और कोड कंस्ट्रक्ट में, दूसरों की तुलना में ज़्यादा मेमोरी का इस्तेमाल होता है. अपने कोड में ज़्यादा बेहतर विकल्प चुनकर, अपने ऐप्लिकेशन के लिए इस्तेमाल की जाने वाली मेमोरी को कम किया जा सकता है.

सेवाओं का कम से कम इस्तेमाल करना

हमारा सुझाव है कि जब सेवाओं की ज़रूरत न हो, तब उन्हें चालू न रखें. ज़रूरत न होने पर भी सेवाओं को चालू रखना, मेमोरी मैनेजमेंट से जुड़ी सबसे खराब गलतियों में से एक है. कोई Android ऐप्लिकेशन ऐसी गलती कर सकता है. अगर आपके ऐप्लिकेशन को बैकग्राउंड में काम करने के लिए किसी सेवा की ज़रूरत है, तो उसे तब तक चालू न रखें, जब तक उसे कोई काम न करना हो. सेवा का काम पूरा होने पर, उसे बंद कर दें. ऐसा न करने पर, मेमोरी लीक हो सकती है.

किसी सेवा को शुरू करने पर, सिस्टम उस सेवा की प्रोसेस को चालू रखता है. इस वजह से, सेवा की प्रोसेस काफ़ी महंगी हो जाती है, क्योंकि सेवा के लिए इस्तेमाल की गई रैम, अन्य प्रोसेस के लिए उपलब्ध नहीं रहती. इससे, सिस्टम में कैश की गई प्रोसेस की संख्या कम हो जाती है. इसलिए, सिस्टम LRU कैश में कम प्रोसेस सेव कर पाता है. इससे ऐप्लिकेशन स्विच करने की प्रोसेस कम असरदार हो जाती है. इससे सिस्टम में थ्रैशिंग की समस्या भी हो सकती है. ऐसा तब होता है, जब मेमोरी कम हो और सिस्टम, फ़िलहाल चल रही सभी सेवाओं को होस्ट करने के लिए ज़रूरी प्रोसेस को बनाए न रख पाए.

आम तौर पर, लगातार काम करने वाली सेवाओं का इस्तेमाल करने से बचें. ऐसा इसलिए, क्योंकि ये सेवाएं उपलब्ध मेमोरी पर लगातार दबाव डालती हैं. इसके बजाय, हमारा सुझाव है कि आप किसी दूसरे तरीके का इस्तेमाल करें. जैसे, WorkManager. बैकग्राउंड प्रोसेस को शेड्यूल करने के लिए, WorkManager का इस्तेमाल करने के तरीके के बारे में ज़्यादा जानने के लिए, परसिस्टेंट वर्क देखें.

ऑप्टिमाइज़ किए गए डेटा कंटेनर का इस्तेमाल करना

प्रोग्रामिंग लैंग्वेज की कुछ क्लास, मोबाइल डिवाइसों पर इस्तेमाल करने के लिए ऑप्टिमाइज़ नहीं की गई हैं. उदाहरण के लिए, सामान्य HashMap को लागू करने पर, मेमोरी का इस्तेमाल ज़्यादा हो सकता है. ऐसा इसलिए, क्योंकि इसे हर मैपिंग के लिए अलग एंट्री ऑब्जेक्ट की ज़रूरत होती है.

Android फ़्रेमवर्क में, ऑप्टिमाइज़ किए गए कई डेटा कंटेनर शामिल होते हैं. इनमें SparseArray, SparseBooleanArray, और LongSparseArray शामिल हैं. उदाहरण के लिए, SparseArray क्लास ज़्यादा असरदार होती हैं. ऐसा इसलिए, क्योंकि ये सिस्टम को कुंजी और कभी-कभी वैल्यू को अपने-आप बॉक्स में रखने की ज़रूरत से बचाती हैं. इससे हर एंट्री के लिए एक या दो और ऑब्जेक्ट बन जाते हैं.

अगर ज़रूरी हो, तो डेटा स्ट्रक्चर को छोटा करने के लिए, हमेशा रॉ ऐरे पर स्विच किया जा सकता है.

कोड ऐब्स्ट्रैक्शन का इस्तेमाल सावधानी से करें

डेवलपर अक्सर ऐब्स्ट्रैक्शन का इस्तेमाल, प्रोग्रामिंग के अच्छे तरीके के तौर पर करते हैं. ऐसा इसलिए, क्योंकि इससे कोड को ज़्यादा आसानी से बदला जा सकता है और उसे मैनेज करना आसान हो जाता है. हालांकि, आम तौर पर ऐब्स्ट्रैक्शन को लागू करने के लिए ज़्यादा कोड की ज़रूरत होती है. अपने ऐप्लिकेशन के कोड और संसाधन के फ़ुटप्रिंट को कम करें लेख में बताया गया है कि कंपाइल किए गए बड़े कोडबेस से, आपके ऐप्लिकेशन के लिए ज़रूरी फ़िज़िकल रैम सीधे तौर पर बढ़ जाती है. अगर आपके ऐब्स्ट्रैक्शन से कोई खास फ़ायदा नहीं मिलता है, तो उनका इस्तेमाल न करें.

सीरियलाइज़ किए गए डेटा के लिए, लाइट प्रोटोबफ़ का इस्तेमाल करना

प्रोटोकॉल बफ़र (protobufs), भाषा और प्लैटफ़ॉर्म से अलग, एक्सटेंसिबल मैकेनिज़्म होते हैं. इन्हें Google ने स्ट्रक्चर्ड डेटा को क्रम में लगाने के लिए डिज़ाइन किया है. यह एक्सएमएल की तरह ही होता है, लेकिन छोटा, तेज़, और आसान होता है. अगर डेटा के लिए प्रोटोबफ़ का इस्तेमाल किया जाता है, तो क्लाइंट-साइड कोड में हमेशा लाइट प्रोटोबफ़ का इस्तेमाल करें. सामान्य प्रोटोबफ़, बहुत ज़्यादा वर्बोस कोड जनरेट करते हैं. इससे रैम में आपके ऐप्लिकेशन के कोड का फ़ुटप्रिंट बढ़ जाता है. (अपने ऐप्लिकेशन के कोड और संसाधन के फ़ुटप्रिंट को कम करें देखें). साथ ही, इससे APK का साइज़ भी बढ़ जाता है.

ज़्यादा जानकारी के लिए, protobuf readme देखें.

मेमोरी लीक से सावधान रहें

रेफ़रंस को सही तरीके से मैनेज न करने पर, मेमोरी लीक हो सकती है. ऐसा तब होता है, जब ऑब्जेक्ट अपने काम की अवधि से ज़्यादा समय तक मौजूद रहते हैं. इससे गार्बेज कलेक्टर, लीक हुए ऑब्जेक्ट की मेमोरी को वापस नहीं पा पाता. मेमोरी लीक से बचने के लिए, लाइफ़साइकल के बारे में जानकारी रखने वाला डिज़ाइन लागू करें.

मेमोरी चर्न से बचें

गारबेज कलेक्शन इवेंट से, आपके ऐप्लिकेशन की परफ़ॉर्मेंस पर कोई असर नहीं पड़ता. हालांकि, कम समय में होने वाले कई गार्बेज कलेक्शन इवेंट से बैटरी तेज़ी से खत्म हो सकती है. साथ ही, गार्बेज कलेक्टर और ऐप्लिकेशन थ्रेड के बीच ज़रूरी इंटरैक्शन की वजह से, फ़्रेम सेट अप करने में थोड़ा समय लग सकता है. सिस्टम जितना ज़्यादा समय गार्बेज कलेक्शन में बिताता है, बैटरी उतनी ही तेज़ी से खत्म होती है.

अक्सर, मेमोरी चर्न की वजह से, गार्बेज कलेक्शन के कई इवेंट हो सकते हैं. मेमोरी चर्न से पता चलता है कि किसी तय समय में, कितने अस्थायी ऑब्जेक्ट बनाए गए.

उदाहरण के लिए, किसी for लूप में एक से ज़्यादा अस्थायी ऑब्जेक्ट असाइन किए जा सकते हैं. इसके अलावा, व्यू के onDraw() फ़ंक्शन में नए Paint या Bitmap ऑब्जेक्ट बनाए जा सकते हैं. इन दोनों ही मामलों में, ऐप्लिकेशन बहुत कम समय में बड़ी संख्या में ऑब्जेक्ट बनाता है. ये ऑब्जेक्ट, जनरेशन 0 में मौजूद मेमोरी को तुरंत इस्तेमाल कर सकते हैं. इससे गार्बेज कलेक्शन इवेंट ट्रिगर हो जाता है.

मेमोरी प्रोफ़ाइलर का इस्तेमाल करके, अपने कोड में उन जगहों का पता लगाएं जहां मेमोरी का इस्तेमाल ज़्यादा होता है. इससे आपको उन जगहों को ठीक करने में मदद मिलेगी.

अपने कोड में समस्या वाले सेक्शन की पहचान करने के बाद, परफ़ॉर्मेंस के लिहाज़ से ज़रूरी सेक्शन में मेमोरी के इस्तेमाल को कम करने की कोशिश करें. चीज़ों को इनर लूप से बाहर ले जाएं या उन्हें फ़ैक्ट्री-आधारित असाइनमेंट स्ट्रक्चर में ले जाएं.

यह भी देखा जा सकता है कि ऑब्जेक्ट पूल, इस्तेमाल के उदाहरण के लिए फ़ायदेमंद हैं या नहीं. ऑब्जेक्ट पूल की मदद से, किसी ऑब्जेक्ट इंस्टेंस को फ़्लोर पर छोड़ने के बजाय, उसे पूल में रिलीज़ किया जाता है. ऐसा तब किया जाता है, जब उसकी ज़रूरत नहीं होती. अगली बार जब उस टाइप के किसी ऑब्जेक्ट इंस्टेंस की ज़रूरत हो, तो उसे ऐलोकेट करने के बजाय पूल से हासिल किया जा सकता है.

परफ़ॉर्मेंस का अच्छी तरह से आकलन करें, ताकि यह तय किया जा सके कि किसी दी गई स्थिति में ऑब्जेक्ट पूल सही है या नहीं. कुछ मामलों में, ऑब्जेक्ट पूल की वजह से परफ़ॉर्मेंस खराब हो सकती है. पूल, मेमोरी को बार-बार ऐलोकेट करने से बचते हैं. हालांकि, इससे अन्य ओवरहेड बढ़ जाते हैं. उदाहरण के लिए, पूल को बनाए रखने के लिए आम तौर पर सिंक्रनाइज़ेशन की ज़रूरत होती है. इसमें काफ़ी समय लगता है. इसके अलावा, रिलीज़ के दौरान मेमोरी लीक से बचने के लिए, पूल किए गए ऑब्जेक्ट इंस्टेंस को साफ़ करने और फिर अधिग्रहण के दौरान उसे शुरू करने में कुछ समय लग सकता है.

पूल में ज़रूरत से ज़्यादा ऑब्जेक्ट इंस्टेंस रखने से, गार्बेज कलेक्शन पर भी असर पड़ता है. ऑब्जेक्ट पूल, गार्बेज कलेक्शन के लिए किए जाने वाले अनुरोधों की संख्या को कम करते हैं. हालांकि, इससे हर अनुरोध के लिए ज़रूरी काम बढ़ जाता है, क्योंकि यह लाइव (पहुंचे जा सकने वाले) बाइट की संख्या के हिसाब से होता है.