मेमोरी कम करना

Android के मेमोरी मैनेजमेंट को समझने और अपने गेम की मेमोरी के इस्तेमाल को मेज़र करने वाले टूल सेट अप करने के बाद, अगला चरण इसे कम करना और ऑप्टिमाइज़ करना है. Android की तय की गई सीमाओं का पालन करने से, सिस्टम आपके गेम को बंद नहीं करता. साथ ही, गेम को चालू होने में ज़्यादा समय नहीं लगता. इससे यह भी पक्का होता है कि आपका गेम सभी डिवाइसों पर ठीक से काम करे.

इस गाइड में, गेम की मेमोरी के इस्तेमाल को कम करने के लिए काम की तकनीकें बताई गई हैं. इसमें खास तौर पर, ऐसेट-लेवल ऑप्टिमाइज़ेशन, इंजन के हिसाब से कॉन्फ़िगरेशन, और मेमोरी मैनेजमेंट के सबसे सही तरीकों पर फ़ोकस किया गया है.

Unity में मेमोरी का इस्तेमाल कम करना

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

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

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

ज़्यादा जानकारी के लिए, Unity में मेमोरी ऑप्टिमाइज़ेशन देखें.

Unreal Engine में मेमोरी का इस्तेमाल कम करना

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

इसलिए, Unreal Engine में मेमोरी ऑप्टिमाइज़ेशन के लिए, इन तीन मुख्य सिद्धांतों का पालन करना ज़रूरी है. ये सिद्धांत, इंजन के व्यवहार के हिसाब से बनाए गए हैं:

  • डेटा और लॉजिक को अलग करें. साथ ही, हार्ड या स्ट्रॉन्ग रेफ़रंस को सॉफ्ट या वीक रेफ़रंस से बदलें.
  • मोबाइल लाइटिंग की ऐसी सुविधाओं और क्रमपरिवर्तन के विकल्पों को हटाना जिनका इस्तेमाल नहीं किया जाता है, ताकि पाइपलाइन स्टेट ऑब्जेक्ट (पीएसओ) और फ़ालतू रेंडर टारगेट को कम किया जा सके. साथ ही, डिवाइस प्रोफ़ाइलों के साथ एएसटीसी कंप्रेशन लागू करना और टेक्सचर स्ट्रीमिंग पूल को पसंद के मुताबिक बनाना.
  • ईएलएफ़ बाइनरी के साइज़ को कम करने और रनटाइम फ़िज़िकल मेमोरी फ़ुटप्रिंट को कम करने के लिए, RELR और APS रिलोकेशन टेबल कंप्रेशन चालू करें.

ज़्यादा जानकारी के लिए, Unreal में मेमोरी ऑप्टिमाइज़ेशन लेख पढ़ें.

एक से ज़्यादा प्रोसेस को ऑप्टिमाइज़ करना

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

ज़्यादा जानकारी के लिए, प्रोसेस की स्थिति और मेमोरी को ट्रैक करने का तरीका, Unity की मदद से किसी सेवा की प्रोसेस को अलग करने का तरीका, और Unreal की मदद से किसी सेवा की प्रोसेस को अलग करने का तरीका देखें.

उपयोगकर्ता को दिखने वाली सेवाओं में मेमोरी का इस्तेमाल कम करना

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

बड़ी फ़ाइलों को डाउनलोड करने की रणनीतियां

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

1. डाउनलोड प्रोसेस को अलग करें

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

  • Unity में: डाउनलोड को नेटिव Android Service पर ऑफ़लोड करें. इसे कस्टम मेनिफ़ेस्ट में android:process=":downloader" जैसी प्रोसेस के साथ डिक्लेयर किया गया है. साथ ही, इसे Unity के AndroidJavaClass JNI के साथ लागू करें. डाउनलोड पूरा होने के बाद, प्रोसेस को बंद करना न भूलें. ज़्यादा जानकारी के लिए, Unity की मदद से, अलग प्रोसेस में ऐसी सेवा चलाना जिसे आसानी से समझा जा सके लेख पढ़ें.

  • Unreal में: Unreal Plugin Language का इस्तेमाल करके, Android के लिए कस्टम Service का एलान करें. इसके लिए, android:process=":downloader" जैसी प्रोसेस का इस्तेमाल करें. इसके बाद, C++ JNI की मदद से इसे ट्रिगर करें. डाउनलोड पूरा होने पर, प्रोसेस को बंद करना न भूलें. ज़्यादा जानकारी के लिए, Unreal की मदद से, अलग प्रोसेस में एक ऐसी सेवा चलाना जिसे आसानी से समझा जा सके लेख पढ़ें.

  • नेटिव Android के लिए: AndroidManifest में Service का एलान करें. इसके लिए, android:process=":downloader" जैसे प्रोसेस का इस्तेमाल करें. इस अलग प्रोसेस में डाउनलोड करें और डाउनलोड पूरा होने पर Process.killProcess(Process.myPid()) को कॉल करें.

इससे क्या फ़ायदा होता है: इससे मेमोरी को कम समय के लिए सेव किया जाता है. साथ ही, बड़ी मुख्य प्रोसेस के लिए इस्तेमाल की गई मेमोरी को रिलीज़ करते समय, डाउनलोड जारी रखने की अनुमति मिलती है.

2. डाउनलोड की गई स्ट्रीम को सीधे डिस्क पर सेव करना

क्या: नेटवर्क सॉकेट से डिस्क में डेटा को सीधे तौर पर स्ट्रीम करें. इसके लिए, नेटवर्क रिस्पॉन्स को लिखने से पहले बड़े ऐरे में इकट्ठा करने के बजाय, छोटे और तय साइज़ के रीयूज़ेबल बफ़र का इस्तेमाल करें.

  • Unity में: ऐसेट बंडल या बड़ी फ़ाइलों के लिए, DownloadHandlerBuffer का इस्तेमाल न करें. ऐसा इसलिए, क्योंकि यह फ़ाइल के साइज़ (Anonymous RSS मेमोरी) के बराबर नेटिव मेमोरी बफ़र असाइन करता है. इसके बजाय, DownloadHandlerFile का इस्तेमाल करके, बैकग्राउंड थ्रेड पर डिस्क में बाइट को नेटिव तरीके से स्ट्रीम करें.

  • Unreal में: TArray<uint8> में IHttpRequest से पेलोड जोड़ने के बजाय, SetResponseBodyReceiveStream() का इस्तेमाल करके, डेटा के आने वाले चंक को सीधे FArchive (Unreal के फ़ाइल मैनेजर का इस्तेमाल करके फ़ाइल-बैकअप वाला संग्रह) में पाइप करें.

  • नेटिव Android के लिए: एचटीटीपी रिस्पॉन्स पर .readBytes() या .string() को कॉल करने के बजाय, InputStream को पूल किए गए बफ़र का इस्तेमाल करके FileOutputStream पर पाइप करें.

इससे क्या मदद मिलती है: इससे मेमोरी का इस्तेमाल कम होता है

3. डाउनलोड की गई फ़ाइल को डीकंप्रेस करने की सुविधा को स्ट्रीम करना

क्या: अगर डाउनलोड की गई फ़ाइल कंप्रेस की गई है, तो फ़ाइल को डाउनलोड करने, उसे रैम में लोड करने, और फिर उसे निकालने के बजाय, अपने नेटवर्क इनपुट स्ट्रीम को ZipInputStream जैसे स्ट्रीमिंग डीकंप्रेसर में रैप करें.

इससे क्या मदद मिलती है: इससे मेमोरी का इस्तेमाल कम होता है

4. ओएस को ऐक्सेस देना

क्या: बैकग्राउंड मेमोरी को मैनेज करने से बचने के लिए, Android के नेटिव एपीआई को काम सौंपें.

  • WorkManager, ओएस-लेवल JobScheduler के लिए, आधुनिक और सुझाया गया रैपर है. Android 14 और इसके बाद के वर्शन के लिए, WorkManager उपयोगकर्ता के ट्रिगर किए गए डाउनलोड को, User-Initiated Data Transfer (UIDT) जॉब के तौर पर अपने-आप मैनेज करता है. यह आपके ऐप्लिकेशन की प्रोसेस में चलता है. इसलिए, मेमोरी का इस्तेमाल कम करने के लिए, आपको अब भी सीधे डिस्क पर स्ट्रीम करना होगा. UIDT, आपके ऐप्लिकेशन को कम मेमोरी की वजह से क्रैश होने से बचाता है. इसके लिए, यह ओएस को यह अनुमति देता है कि अगर सिस्टम के संसाधन सीमित हो जाएं, तो वह डाउनलोड को कुछ समय के लिए रोक दे और फिर से शुरू कर दे.

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

इससे क्या फ़ायदा होता है: WorkManager से, कम मेमोरी की समस्या को हल करने में मदद मिलती है. साथ ही, DownloadManager से, आपके ऐप्लिकेशन के मेमोरी इस्तेमाल को कम किया जा सकता है.

सहायक सेवाओं के लिए रणनीतियां

ये रणनीतियां, उन सहायक सेवाओं पर लागू हो सकती हैं जिन्हें गेम, मुख्य गेम प्रोसेस के साथ-साथ चलाता है. जैसे, बैकग्राउंड में वॉइस चैट.

1. प्रोसेस को अलग करना

क्या: सुविधा को मुख्य गेम इंजन से अलग करें. उदाहरण के लिए, वॉइस चैट की सुविधा. उदाहरण के लिए, किसी अलग प्रोसेस को असाइन की गई Android फ़ोरग्राउंड सेवा में, माइक्रोफ़ोन कैप्चर और नेटवर्क स्ट्रीमिंग की जा सकती है. इस प्रोसेस के बारे में मेनिफ़ेस्ट में बताया गया होता है. जैसे, android:process=":voice".

इससे क्या फ़ायदा होता है: जब ऐप्लिकेशन को छोटा किया जाता है, तो गेम की मुख्य प्रोसेस को कम प्राथमिकता वाली कैश्ड स्थिति में रखा जा सकता है. वहीं, हल्की-फुल्की सहायक सेवा, उपयोगकर्ता को दिखने वाली सेवा की स्थिति में बनी रहती है.

2. इस्तेमाल न की गई मेमोरी को ट्रिम करें

क्या: अगर गेम इंजन में सहायक सेवा को इस तरह से इंटिग्रेट किया गया है कि उसे अलग नहीं किया जा सकता, तो गेम के बैकग्राउंड में जाने या छोटा होने पर, प्रोसेस का इस्तेमाल कम से कम करने की कोशिश करें. टेक्सचर कैश मेमोरी को फ़्लश करें, गैर-ज़रूरी सीन को अनलोड करें, इंजन टिक और रेंडर रेट को 0 पर सेट करें, और गार्बेज कलेक्शन को साफ़ तौर पर कॉल करें.

  • Unity में: OnApplicationPause() ट्रिगर होने पर, इस ट्रिमिंग को लागू करें. Resources.UnloadUnusedAssets() से मदद मिल सकती है.

  • Unreal में: ट्रिमिंग लॉजिक को ApplicationWillEnterBackgroundDelegate डेलिगेट से बाइंड करें.

  • नेटिव Android के लिए: ज़रूरत के मुताबिक, onPause() या onStop() में ट्रिमिंग करें. ओएस, ज़्यादा मेमोरी का इस्तेमाल करने वाली प्रोसेस को बंद करने से पहले, onTrimMemory() को लागू करने की कोशिश कर सकता है.

इससे क्या फ़ायदा होता है: जब गेम फ़ोरग्राउंड में नहीं होता है, तब यह ऐसी मेमोरी के इस्तेमाल को कम करता है जिसकी ज़रूरत नहीं होती.