एलएमके डीबग करना

अपने Unity गेम में एलएमके को ठीक करने के लिए, यह तरीका अपनाएं:

पहली इमेज. Unity गेम में Low Memory Kills (LMK) की समस्या को हल करने का तरीका.

मेमोरी स्नैपशॉट पाना

Unity की मैनेज की गई मेमोरी का स्नैपशॉट पाने के लिए, Unity Profiler का इस्तेमाल करें. दूसरी इमेज में, मेमोरी मैनेजमेंट की उन लेयर को दिखाया गया है जिनका इस्तेमाल Unity, आपके गेम में मेमोरी को मैनेज करने के लिए करता है.

दूसरी इमेज. Unity के मेमोरी मैनेजमेंट के बारे में खास जानकारी.

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

Unity का मेमोरी मैनेजमेंट, कंट्रोल की गई मेमोरी लेयर लागू करता है. यह मैनेज किए गए हीप और गार्बेज कलेक्टर का इस्तेमाल करके, मेमोरी को अपने-आप असाइन करता है. मैनेज की गई मेमोरी सिस्टम, C# स्क्रिप्टिंग एनवायरमेंट है. यह Mono या IL2CPP पर आधारित है. मैनेज की गई मेमोरी सिस्टम का फ़ायदा यह है कि यह मेमोरी के बंटवारे को अपने-आप खाली करने के लिए, गार्बेज कलेक्टर का इस्तेमाल करता है.

C# में अनमैनेज्ड मेमोरी

अनमैनेज्ड C# मेमोरी लेयर, नेटिव मेमोरी लेयर का ऐक्सेस देती है. इससे C# कोड का इस्तेमाल करते समय, मेमोरी के बंटवारे पर सटीक कंट्रोल मिलता है. मेमोरी मैनेजमेंट की इस लेयर को Unity.Collections नेमस्पेस के ज़रिए ऐक्सेस किया जा सकता है. साथ ही, इसे UnsafeUtility.Malloc और UnsafeUtility.Free जैसे फ़ंक्शन के ज़रिए भी ऐक्सेस किया जा सकता है.

नेटिव मेमोरी

Unity का इंटरनल C/C++ कोर, नेटिव मेमोरी सिस्टम का इस्तेमाल करता है. इससे सीन, ऐसेट, ग्राफ़िक्स एपीआई, ड्राइवर, सबसिस्टम, और प्लग-इन बफ़र मैनेज किए जाते हैं. हालांकि, सीधे तौर पर ऐक्सेस करने की सुविधा सीमित है. फिर भी, Unity के C# API की मदद से डेटा को सुरक्षित तरीके से बदला जा सकता है. साथ ही, बेहतर नेटिव कोड का फ़ायदा पाया जा सकता है. नेटिव मेमोरी को सीधे तौर पर इंटरैक्ट करने की ज़रूरत बहुत कम पड़ती है. हालांकि, परफ़ॉर्मेंस पर नेटिव मेमोरी के असर को मॉनिटर किया जा सकता है. इसके लिए, Profiler का इस्तेमाल करें. साथ ही, परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के लिए, सेटिंग में बदलाव करें.

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

मैनेज किए गए गेम के कोड (C#) को इंजन के नेटिव मेमोरी डेटा को ऐक्सेस करने के लिए, उदाहरण के लिए, GameObject.transform को कॉल करने पर, नेटिव कॉल किया जाता है. इससे नेटिव एरिया में मौजूद मेमोरी डेटा को ऐक्सेस किया जाता है. इसके बाद, Bindings का इस्तेमाल करके, C# को वैल्यू वापस भेजी जाती हैं. बाइंडिंग यह पक्का करती हैं कि हर प्लैटफ़ॉर्म के लिए, कॉलिंग के सही नियमों का पालन किया जाए. साथ ही, मैनेज किए गए टाइप को उनके नेटिव वर्शन में अपने-आप मार्शेल करती हैं.

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

तीसरी इमेज. C# मैनेज किए गए कोड से नेटिव मेमोरी को ऐक्सेस करना.

ज़्यादा जानने के लिए, Unity में मेमोरी के बारे में जानकारी देखें.

इसके अलावा, मेमोरी बजट सेट करना ज़रूरी है, ताकि आपका गेम बिना किसी रुकावट के चलता रहे. साथ ही, मेमोरी इस्तेमाल करने से जुड़ी आंकड़ों या रिपोर्टिंग सिस्टम को लागू करने से यह पक्का होता है कि हर नई रिलीज़, मेमोरी बजट से ज़्यादा न हो. PlayMode टेस्ट को कंटीन्यूअस इंटिग्रेशन (सीआई) के साथ इंटिग्रेट करके, गेम के कुछ हिस्सों में मेमोरी की खपत की पुष्टि की जा सकती है. यह बेहतर जानकारी पाने का एक और तरीका है.

ऐसेट मैनेज करें

मेमोरी की खपत का यह सबसे अहम और कार्रवाई करने लायक हिस्सा है. प्रोफ़ाइल को जल्द से जल्द अपडेट करें.

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

डुप्लीकेट ऐसेट का पता लगाना

पहला चरण, मेमोरी प्रोफ़ाइलर, बिल्ड रिपोर्ट टूल या प्रोजेक्ट ऑडिटर का इस्तेमाल करके, खराब तरीके से कॉन्फ़िगर की गई ऐसेट और डुप्लीकेट ऐसेट का पता लगाना है.

टेक्स्चर

अपने गेम के लिए, डिवाइस के साथ काम करने वाले टेक्सचर फ़ॉर्मैट का सही तरीके से विश्लेषण करें. Play ऐसेट डिलीवरी, Addressable या AssetBundle के साथ ज़्यादा मैन्युअल प्रोसेस का इस्तेमाल करके, हाई-एंड और लो-एंड डिवाइसों के लिए टेक्सचर बंडल को अलग-अलग किया जा सकता है.

अपने मोबाइल गेम की परफ़ॉर्मेंस ऑप्टिमाइज़ करें और Unity में टेक्सचर इंपोर्ट करने की सेटिंग ऑप्टिमाइज़ करना चर्चा पोस्ट में उपलब्ध सबसे सही सुझावों को अपनाएं. इसके बाद, ये तरीके आज़माएं:

  • मेमोरी के इस्तेमाल को कम करने के लिए, टेक्सचर को एएसटीसी फ़ॉर्मैट में कंप्रेस करें. साथ ही, ज़्यादा ब्लॉक रेट, जैसे कि 8x8 का इस्तेमाल करके एक्सपेरिमेंट करें.

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

  • RenderTarget के टेक्सचर फ़ॉर्मैट और साइज़ को ऑप्टिमाइज़ करें. ज़रूरत से ज़्यादा हाई रिज़ॉल्यूशन वाले टेक्सचर इस्तेमाल न करें. मोबाइल डिवाइसों पर छोटी बनावट वाली इमेज का इस्तेमाल करने से मेमोरी बचती है.

  • टेक्स्चर के लिए मेमोरी को सेव करने के लिए, टेक्स्चर चैनल पैकिंग का इस्तेमाल करें.

मेश और मॉडल

सबसे पहले, बुनियादी सेटिंग (पेज 27) देखें. इसके बाद, मेश इंपोर्ट करने की इन सेटिंग की पुष्टि करें:

  • ज़रूरत से ज़्यादा और छोटे मेश को मर्ज करें.
  • सीन में मौजूद ऑब्जेक्ट के लिए वर्टेक्स की संख्या कम करें. उदाहरण के लिए, स्टैटिक या दूर के ऑब्जेक्ट.
  • ज़्यादा जानकारी वाली ऐसेट के लिए, लेवल ऑफ़ डिटेल (एलओडी) ग्रुप जनरेट करें.

मटेरियल और शेडर

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

ऑडियो

सबसे पहले, बुनियादी सेटिंग (पेज 41) देखें. इसके बाद, मेश इंपोर्ट करने की इन सेटिंग की पुष्टि करें:

  • FMOD या Wwise जैसे तीसरे पक्ष के ऑडियो इंजन का इस्तेमाल करते समय, इस्तेमाल न किए गए या ज़रूरत से ज़्यादा AudioClip रेफ़रंस हटाएं.
  • ऑडियो डेटा को पहले से लोड करना. उन क्लिप के लिए प्रीलोडिंग की सुविधा बंद करें जिनकी ज़रूरत रनटाइम या सीन के शुरू होने के दौरान तुरंत नहीं होती है. इससे सीन को शुरू करते समय, मेमोरी ओवरहेड को कम करने में मदद मिलती है.

ऐनिमेशन

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

रिग या ऐनिमेशन टैब में जाकर, ऐनिमेशन इंपोर्ट करने की सेटिंग में कंप्रेशन की सेटिंग को अडजस्ट किया जा सकता है.

  • अलग-अलग ऑब्जेक्ट के लिए, ऐनिमेशन क्लिप को डुप्लीकेट करने के बजाय, उनका फिर से इस्तेमाल करें.

    ऐनिमेटर ओवरराइड कंट्रोलर का इस्तेमाल करके, ऐनिमेटर कंट्रोलर को फिर से इस्तेमाल किया जा सकता है. साथ ही, अलग-अलग वर्णों के लिए कुछ क्लिप बदली जा सकती हैं.

  • फ़िज़िक्स पर आधारित ऐनिमेशन बेक करें: अगर आपके ऐनिमेशन फ़िज़िक्स पर आधारित हैं या प्रोसीजरल हैं, तो उन्हें ऐनिमेशन क्लिप में बेक करें, ताकि रनटाइम कैलकुलेशन से बचा जा सके.

  • स्केलेटन रिग को ऑप्टिमाइज़ करें: रिग में कम हड्डियों का इस्तेमाल करें, ताकि जटिलता और मेमोरी की खपत कम हो.

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

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

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

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

सीन

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

  • बार-बार इस्तेमाल होने वाले गेमप्ले एलिमेंट के लिए, Unity की ऑब्जेक्ट पूलिंग का इस्तेमाल करें. ऐसा इसलिए, क्योंकि ऑब्जेक्ट पूलिंग, फिर से इस्तेमाल करने के लिए ऑब्जेक्ट इंस्टेंस के कलेक्शन को होल्ड करने के लिए स्टैक का इस्तेमाल करती है और यह थ्रेड सेफ़ नहीं है. Instantiate और Destroy को कम करने से, सीपीयू की परफ़ॉर्मेंस और मेमोरी की स्थिरता, दोनों बेहतर होती हैं.
  • ऐसेट अनलोड की जा रही हैं:
    • स्प्लैश स्क्रीन या लोडिंग स्क्रीन जैसे कम ज़रूरी समय के दौरान, ऐसेट को रणनीतिक तरीके से अनलोड करें.
    • Resources.UnloadUnusedAssets का बार-बार इस्तेमाल करने से, सीपीयू प्रोसेसिंग में अचानक बढ़ोतरी होती है. ऐसा इसलिए होता है, क्योंकि अंदरूनी तौर पर डिपेंडेंसी को मॉनिटर करने की कार्रवाइयां ज़्यादा होती हैं.
    • GC.MarkDependencies प्रोफ़ाइल मार्कर में, सीपीयू के इस्तेमाल में हुए बड़े उतार-चढ़ाव की जांच करें. इसके एक्ज़ीक्यूशन की फ़्रीक्वेंसी कम करें या इसे हटा दें. साथ ही, Resources.UnloadAsset का इस्तेमाल करके, कुछ खास संसाधनों को मैन्युअल तरीके से अनलोड करें. इसके लिए, Resources.UnloadUnusedAssets() पर भरोसा न करें.
  • Resources.UnloadUnusedAssets का लगातार इस्तेमाल करने के बजाय, सीन को फिर से स्ट्रक्चर करें.
  • Resources.UnloadUnusedAssets() को Addressables के लिए कॉल करने से, डाइनैमिक तौर पर लोड किए गए बंडल अनजाने में अनलोड हो सकते हैं. डाइनैमिक तरीके से लोड की गई ऐसेट की लाइफ़साइकल को ध्यान से मैनेज करें.

अन्य चीज़ें

  • सीन ट्रांज़िशन की वजह से फ़्रैगमेंटेशन — जब Resources.UnloadUnusedAssets() तरीके को कॉल किया जाता है, तो Unity ये काम करता है:

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

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

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

  • TypeTrees - अगर आपके गेम के Addressables और AssetBundles को प्लेयर के Unity के वर्शन का इस्तेमाल करके बनाया और डिप्लॉय किया गया है और इसके लिए, प्लेयर के अन्य बिल्ड के साथ बैकवर्ड कंपैटिबिलिटी की ज़रूरत नहीं है, तो TypeTree को लिखने की सुविधा बंद करें. इससे बंडल का साइज़ और सीरियलाइज़ की गई फ़ाइल ऑब्जेक्ट मेमोरी फ़ुटप्रिंट कम हो जाएगा. स्थानीय Addressables पैकेज की सेटिंग ContentBuildFlags में जाकर, बिल्ड प्रोसेस में बदलाव करें. इसके बाद, DisableWriteTypeTree को सेट करें.

गारबेज कलेक्टर के साथ काम करने वाला कोड लिखना

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

मैनेज किए गए हीप के लिए मेमोरी के बार-बार होने वाले असाइनमेंट को कम करने के बारे में जानने के लिए, Unity का मैन्युअल पढ़ें. साथ ही, उदाहरणों के लिए UnityPerformanceTuningBible के पेज 271 पर जाएं.

  • गारबेज कलेक्टर के लिए मेमोरी का आवंटन कम करें:

    • LINQ, लैम्डा, और क्लोज़र का इस्तेमाल न करें. ये हीप मेमोरी को असाइन करते हैं.
    • स्ट्रिंग को जोड़ने के बजाय, स्ट्रिंग में बदलाव करने के लिए StringBuilder का इस्तेमाल करें.
    • कलेक्शन को फिर से इंस्टैंटिएट करने के बजाय, COLLECTIONS.Clear() को कॉल करके उनका फिर से इस्तेमाल करें.

    ज़्यादा जानकारी के लिए, Unity गेम की प्रोफ़ाइलिंग के बारे में पूरी जानकारी देने वाली गाइड ई-बुक पढ़ें.

  • यूज़र इंटरफ़ेस (यूआई) कैनवस के अपडेट मैनेज करना:

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

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

  • गेम की स्थिति में बदलाव होने पर, मैन्युअल गार्बेज कलेक्शन GC.Collect() कॉल शुरू करें. उदाहरण के लिए, लेवल बदलने पर.

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

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

    ऑटोमेटेड अप्रोच के लिए, परफ़ॉर्मेंस रिपोर्टिंग टूल को सबमिट करने के लिए, प्रोफ़ाइलर मार्कर का इस्तेमाल करें.

मेमोरी लीक और फ़्रैगमेंटेशन से बचना

मेमोरी लीक

C# कोड में, जब किसी Unity ऑब्जेक्ट का रेफ़रंस ऑब्जेक्ट के डिस्ट्रॉय होने के बाद मौजूद होता है, तो मैनेज किया गया रैपर ऑब्जेक्ट, जिसे मैनेज्ड शेल कहा जाता है, मेमोरी में बना रहता है. जब सीन को अनलोड किया जाता है या जब मेमोरी से जुड़ा GameObject या उसके किसी पैरंट ऑब्जेक्ट को Destroy() तरीके से डिस्ट्रॉय किया जाता है, तब रेफ़रंस से जुड़ी नेटिव मेमोरी रिलीज़ हो जाती है. हालांकि, अगर सीन या गेमऑब्जेक्ट के अन्य रेफ़रंस नहीं हटाए गए हैं, तो मैनेज की गई मेमोरी लीक हुए शेल ऑब्जेक्ट के तौर पर बनी रह सकती है. मैनेज किए गए शेल ऑब्जेक्ट के बारे में ज़्यादा जानकारी के लिए, मैनेज किए गए शेल ऑब्जेक्ट का मैन्युअल देखें.

इसके अलावा, इवेंट की सदस्यताएं, लैम्डा और क्लोज़र, स्ट्रिंग कॉनकैटेनेशन, और पूल किए गए ऑब्जेक्ट को सही तरीके से मैनेज न करने की वजह से भी मेमोरी लीक हो सकती है:

  • शुरू करने के लिए, मेमोरी लीक ढूंढना लेख पढ़ें, ताकि Unity के मेमोरी स्नैपशॉट की तुलना सही तरीके से की जा सके.
  • इवेंट की सदस्यताएं और मेमोरी लीक की जांच करें. अगर ऑब्जेक्ट, इवेंट के लिए सदस्यता लेते हैं (उदाहरण के लिए, प्रतिनिधियों या UnityEvents के ज़रिए), लेकिन डिस्ट्रॉय होने से पहले सदस्यता नहीं छोड़ते हैं, तो इवेंट मैनेजर या पब्लिशर उन ऑब्जेक्ट के रेफ़रंस सेव कर सकता है. इससे उन ऑब्जेक्ट को गार्बेज कलेक्शन से रोका जाता है. इससे मेमोरी लीक होती है.
  • उन ग्लोबल या सिंगलटन क्लास इवेंट को मॉनिटर करें जिनका ऑब्जेक्ट डिस्ट्रक्शन पर रजिस्ट्रेशन रद्द नहीं किया गया है. उदाहरण के लिए, ऑब्जेक्ट डिस्ट्रक्टर में प्रतिनिधियों की सदस्यता छोड़ना या उन्हें हटाना.
  • पक्का करें कि पूल किए गए ऑब्जेक्ट को डिस्ट्रॉय करने से, टेक्स्ट मेश कॉम्पोनेंट, टेक्सचर, और पैरंट गेमऑब्जेक्ट के रेफ़रंस पूरी तरह से हट जाएं.
  • ध्यान रखें कि Unity Memory Profiler के स्नैपशॉट की तुलना करते समय और मेमोरी इस्तेमाल करने में अंतर की वजह पता न चलने पर, यह अंतर ग्राफ़िक्स ड्राइवर या ऑपरेटिंग सिस्टम की वजह से हो सकता है.

मेमोरी फ़्रैगमेंटेशन

मेमोरी फ़्रैगमेंटेशन तब होता है, जब कई छोटे-छोटे हिस्सों को किसी भी क्रम में खाली कर दिया जाता है. ढेर में मेमोरी को क्रम से बांटा जाता है. इसका मतलब है कि जब पिछले हिस्से में जगह खत्म हो जाती है, तब मेमोरी के नए हिस्से बनाए जाते हैं. इसलिए, नए ऑब्जेक्ट पुराने चंक की खाली जगहों को नहीं भरते हैं. इससे फ़्रैगमेंटेशन होता है. इसके अलावा, कुछ समय के लिए बहुत ज़्यादा मेमोरी असाइन करने से, गेम के सेशन के दौरान मेमोरी का फ़्रैगमेंटेशन हो सकता है.

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

लाइफ़स्पैन के आधार पर ग्रुप असाइनमेंट; सबसे सही तरीका यह है कि लंबे समय तक इस्तेमाल होने वाले असाइनमेंट को ऐप्लिकेशन के लाइफ़साइकल की शुरुआत में एक साथ किया जाए.

ऑब्ज़र्वर और इवेंट मैनेजर

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

कोड

  • कोरूटीन कभी-कभी मेमोरी असाइन करते हैं. हर बार नया कोरूटीन बनाने के बजाय, IEnumerator के रिटर्न स्टेटमेंट को कैश करके, इस समस्या से आसानी से बचा जा सकता है.
  • पूल किए गए ऑब्जेक्ट की लाइफ़साइकल की स्थितियों पर लगातार नज़र रखें, ताकि UnityEngine.Object घोस्ट रेफ़रंस न बने रहें.

एसेट

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

लाइफ़टाइम के आधार पर असाइन किए गए बजट

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

गेम के एक्ज़ीक्यूटेबल और प्लगिन भी मेमोरी के इस्तेमाल पर असर डालते हैं.

IL2CPP मेटाडेटा

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

IL2CPP मेटाडेटा को इन तरीकों से कम किया जा सकता है:

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

कोड स्ट्रिपिंग

कोड स्ट्रिपिंग से, बिल्ड का साइज़ कम होने के साथ-साथ मेमोरी का इस्तेमाल भी कम होता है. IL2CPP स्क्रिप्टिंग बैकएंड के ख़िलाफ़ बिल्ड करते समय, मैनेज किए गए बाइटकोड को हटाने की सुविधा (जो डिफ़ॉल्ट रूप से चालू होती है) मैनेज की गई असेंबलियों से इस्तेमाल न होने वाले कोड को हटा देती है. यह प्रोसेस, रूट असेंबली तय करके काम करती है. इसके बाद, स्टैटिक कोड विश्लेषण का इस्तेमाल करके यह पता लगाया जाता है कि रूट असेंबली, मैनेज किए गए अन्य कोड का इस्तेमाल कैसे करती हैं. ऐसे कोड को हटा दिया जाता है जिस तक पहुंचा नहीं जा सकता. मैनेज किए गए कोड स्ट्रिपिंग के बारे में ज़्यादा जानने के लिए, TTales from the optimization trenches: Better managed code stripping with Unity 2020 LTS ब्लॉग पोस्ट और मैनेज किए गए कोड स्ट्रिपिंग दस्तावेज़ देखें.

नेटिव ऐलोकेटर

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

नेटिव प्लगिन और एसडीके मैनेज करना

  • समस्या पैदा करने वाला प्लगिन ढूंढें — हर प्लगिन को हटाएं और गेम की मेमोरी के स्नैपशॉट की तुलना करें. इसमें Scripting Define Symbols की मदद से, कोड की कई सुविधाओं को बंद करना और इंटरफ़ेस की मदद से, एक-दूसरे से जुड़ी क्लास को फिर से व्यवस्थित करना शामिल है. गेम प्रोग्रामिंग पैटर्न की मदद से अपने कोड को बेहतर बनाएं लेख पढ़ें. इससे आपको गेम को बिना खेले जाने की स्थिति में लाए, बाहरी डिपेंडेंसी को बंद करने की प्रोसेस को आसान बनाने में मदद मिलेगी.

  • प्लगिन या एसडीके टूल के लेखक से संपर्क करें — ज़्यादातर प्लगिन ओपन सोर्स नहीं होते हैं.

  • प्लगिन के मेमोरी का इस्तेमाल करने की प्रोसेस को दोहराएं — मेमोरी का बंटवारा करने वाला एक आसान प्लगिन लिखें. इसके लिए, इस Unity प्लगिन का इस्तेमाल रेफ़रंस के तौर पर करें. Android Studio का इस्तेमाल करके, मेमोरी स्नैपशॉट की जांच करें. ऐसा इसलिए, क्योंकि Unity इन ऐलोकेशन को ट्रैक नहीं करता है. इसके अलावा, एक ही प्रोजेक्ट में MemoryInfo क्लास और Runtime.totalMemory() तरीके को कॉल करें.

Unity प्लगिन, Java और नेटिव मेमोरी असाइन करता है. इसे असाइन करने का तरीका यहां बताया गया है:

Java

byte[] largeObject = new byte[1024 * 1024 * megaBytes];
list.add(largeObject);

नेटिव

char* buffer = new char[megabytes * 1024 * 1024];

// Random data to fill the buffer
for (int i = 1; i < megabytes * 1024 * 1024; ++i) {
   buffer[i] = 'A' + (i % 26); // Fill with letters A-Z
}