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

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

रैंडम-ऐक्सेस मेमोरी (रैम), सॉफ़्टवेयर डेवलपमेंट के किसी भी एनवायरमेंट के लिए एक अहम संसाधन है. यह मोबाइल ऑपरेटिंग सिस्टम के लिए और भी अहम है, क्योंकि इसमें अक्सर फ़िज़िकल मेमोरी सीमित होती है. Android Runtime (एआरटी) और 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 कॉन्फ़िगरेशन का इस्तेमाल करते हैं. इसका मतलब है कि हर पिक्सल के लिए 4 बाइट मेमोरी की ज़रूरत होती है. इसमें लाल, हरे, नीले, और अल्फ़ा (पारदर्शिता) के लिए एक-एक बाइट मेमोरी की ज़रूरत होती है. अगर आपके पास 100 केबी की JPEG इमेज है और उसे 1000×1000 पिक्सल व्यू में दिखाया जाता है, तो बिटमैप को उन 10 लाख पिक्सल में से हर एक के लिए 4 बाइट की ज़रूरत होगी. इससे 4 एमबी मेमोरी का इस्तेमाल होगा.

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

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

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

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

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

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

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 ऑब्जेक्ट बनाए जा सकते हैं. दोनों ही मामलों में, ऐप्लिकेशन तेज़ी से और ज़्यादा संख्या में ऑब्जेक्ट बनाता है. इससे, युवा जनरेशन में उपलब्ध सभी मेमोरी तेज़ी से खत्म हो सकती है. इसलिए, गार्बेज कलेक्शन इवेंट होने की संभावना बढ़ जाती है.

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

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

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

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

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