परिचय
आपको एक ईमेल मिलता है, जिसमें बताया जाता है कि आपका वेब-गेम / वेब-ऐप्लिकेशन, कुछ समय बाद खराब परफ़ॉर्म कर रहा है. इसके बाद, आप अपने कोड को देखते हैं, लेकिन आपको कोई खास जानकारी नहीं मिलती. हालांकि, Chrome के मेमोरी परफ़ॉर्मेंस टूल खोलने पर, आपको यह जानकारी मिलती है:
आपका कोई सहकर्मी हंसता है, क्योंकि उसे पता चलता है कि आपके डिवाइस की परफ़ॉर्मेंस में, मेमोरी से जुड़ी समस्या है.
मेमोरी ग्राफ़ व्यू में, यह आरी-टूथ पैटर्न, परफ़ॉर्मेंस की एक गंभीर समस्या के बारे में बता रहा है. मेमोरी के इस्तेमाल में बढ़ोतरी होने पर, आपको टाइमलाइन कैप्चर में चार्ट का क्षेत्र भी बढ़ता हुआ दिखेगा. जब चार्ट में अचानक गिरावट आती है, तो इसका मतलब है कि गै़रबेज कलेक्टर ने काम किया है और आपके रेफ़रंस मेमोरी ऑब्जेक्ट को साफ़ कर दिया है.
इस तरह के ग्राफ़ में, देखा जा सकता है कि कचरा इकट्ठा करने वाले कई इवेंट हो रहे हैं. इनसे आपके वेब ऐप्लिकेशन की परफ़ॉर्मेंस को नुकसान पहुंच सकता है. इस लेख में, मेमोरी के इस्तेमाल को कंट्रोल करने का तरीका बताया गया है, ताकि आपकी परफ़ॉर्मेंस पर पड़ने वाले असर को कम किया जा सके.
कचरा इकट्ठा करने का तरीका और परफ़ॉर्मेंस पर होने वाला खर्च
JavaScript का मेमोरी मॉडल, गार्बेज कलेक्टर नाम की टेक्नोलॉजी पर आधारित है. कई भाषाओं में, प्रोग्रामर की सीधे तौर पर यह ज़िम्मेदारी होती है कि वह सिस्टम के मेमोरी हेप से मेमोरी को ऐलोकेट और फ़्री करे. हालांकि, प्रोग्रामर की ओर से यह काम, गै़रबेज कलेक्टर सिस्टम मैनेज करता है. इसका मतलब है कि जब प्रोग्रामर किसी ऑब्जेक्ट का रेफ़रंस हटाता है, तो वह ऑब्जेक्ट सीधे मेमोरी से हटाया नहीं जाता. बल्कि, बाद में जब जीसी के हेयुरिस्टिक्स तय करते हैं कि ऐसा करना फ़ायदेमंद होगा, तब ऑब्जेक्ट हटाया जाता है. इस फ़ैसले की प्रोसेस के लिए, जीसी को चालू और बंद ऑब्जेक्ट पर कुछ आंकड़ों का विश्लेषण करना होता है. इसमें कुछ समय लगता है.
गै़रबेज कलेक्शन को अक्सर मैन्युअल मेमोरी मैनेजमेंट के ठीक उलटा दिखाया जाता है. इसके लिए, प्रोग्रामर को यह बताना पड़ता है कि कौनसे ऑब्जेक्ट को हटाना है और उन्हें मेमोरी सिस्टम में वापस लाना है
जिस प्रक्रिया में जीसी वापस मेमोरी लेता है वह मुफ़्त नहीं होती. आम तौर पर, यह अपने काम को करने के लिए कुछ समय लेता है, जिससे आपकी उपलब्ध परफ़ॉर्मेंस में कटौती होती है. इसके साथ-साथ, सिस्टम खुद ही यह तय करता है कि उसे कब चलाना है. इस कार्रवाई पर आपका कोई कंट्रोल नहीं होता. कोड लागू होने के दौरान, किसी भी समय जीसी पल्स हो सकता है. यह पल्स, कोड लागू होने तक उसे ब्लॉक कर देगा. आम तौर पर, आपको इस पल्से की अवधि के बारे में नहीं पता होता. इसे चलने में कुछ समय लगेगा. यह इस बात पर निर्भर करता है कि आपका प्रोग्राम किसी भी समय मेमोरी का इस्तेमाल कैसे कर रहा है.
बेहतर परफ़ॉर्मेंस वाले ऐप्लिकेशन, परफ़ॉर्मेंस की सीमाओं पर निर्भर रहते हैं, ताकि उपयोगकर्ताओं को बेहतर अनुभव मिल सके. गै़रबेज कलेक्टर सिस्टम इस लक्ष्य को पूरा करने में रुकावट डाल सकते हैं. ऐसा इसलिए, क्योंकि ये सिस्टम किसी भी समय और किसी भी अवधि के लिए चल सकते हैं. इससे, ऐप्लिकेशन को परफ़ॉर्मेंस के लक्ष्यों को पूरा करने के लिए उपलब्ध समय कम हो जाता है.
मेमोरी चर्न आउट कम करना, कचरा इकट्ठा करने पर लगने वाले टैक्स कम करना
जैसा कि बताया गया है, जीसी पल्स तब होता है, जब अनुभव के हिसाब से यह पता चलता है कि ज़रूरत के हिसाब से ऐक्टिव ऑब्जेक्ट हैं, जिनके लिए पल्स का असर पड़ सकता है. इसलिए, आपके ऐप्लिकेशन में गै़रबेज कलेक्टर को कम से कम समय देने के लिए, ज़रूरत से ज़्यादा ऑब्जेक्ट बनाने और रिलीज़ करने की प्रक्रिया को कम से कम करें. ऑब्जेक्ट बनाने या हटाने की इस प्रोसेस को अक्सर “मेमोरी चर्न आउट” कहा जाता है. अगर ऐप्लिकेशन के बने रहने के दौरान मेमोरी चर्न आउट को कम किया जा सकता है, तो इससे उस समय को भी कम किया जा सकता है जो जीसी को लागू होने में लगता है. इसका मतलब है कि आपको बनाए गए और नष्ट किए गए ऑब्जेक्ट की संख्या को हटाना या कम करना होगा. इसका मतलब है कि आपको मेमोरी को असाइन करना बंद करना होगा.
इस प्रोसेस से, आपके 'यादें' ग्राफ़ को यहां से यहां ले जाया जाएगा:
इस पर जाएं:
इस मॉडल में, यह देखा जा सकता है कि ग्राफ़ में अब सैवटूथ जैसा पैटर्न नहीं है. इसके बजाय, शुरुआत में यह काफ़ी बढ़ता है और फिर समय के साथ धीरे-धीरे बढ़ता है. अगर आपको मेमोरी में बदलाव की वजह से परफ़ॉर्मेंस से जुड़ी समस्याएं आ रही हैं, तो आपको इस तरह का ग्राफ़ बनाना होगा.
स्टैटिक-मेमोरी JavaScript की ओर बढ़ना
स्टैटिक मेमोरी JavaScript ऐसी तकनीक है जिसमें आपके ऐप्लिकेशन की शुरुआत में, पहले से तय करना शामिल होता है. इसमें, उस मेमोरी की ज़रूरत होती है जिसे अब तक इस्तेमाल किया जा सकता है. साथ ही, मेमोरी को ऑब्जेक्ट के तौर पर मैनेज करने की ज़रूरत नहीं होती. इस लक्ष्य को कुछ आसान चरणों में पूरा किया जा सकता है:
- अपने ऐप्लिकेशन को इंस्ट्रूमेंट करें, ताकि यह पता लगाया जा सके कि इस्तेमाल के अलग-अलग मामलों के लिए, हर टाइप के लाइव मेमोरी ऑब्जेक्ट की ज़्यादा से ज़्यादा संख्या क्या है
- ज़्यादा से ज़्यादा रकम को पहले से तय करने के लिए, अपना कोड फिर से लागू करें. इसके बाद, मुख्य मेमोरी में जाने के बजाय, उन्हें मैन्युअल तरीके से फ़ेच/रिलीज़ करें.
असल में, #1 को पूरा करने के लिए, हमें #2 का थोड़ा-बहुत इस्तेमाल करना होगा. इसलिए, हम वहीं से शुरू करते हैं.
ऑब्जेक्ट पूल
आसान शब्दों में, ऑब्जेक्ट पूल करने का मतलब है, एक ही टाइप के इस्तेमाल नहीं किए गए ऑब्जेक्ट का सेट बनाए रखना. जब आपको सिस्टम के Memory Heap से नया ऑब्जेक्ट असाइन करने के बजाय, अपने कोड के लिए कोई नया ऑब्जेक्ट की ज़रूरत हो, तो पूल के इस्तेमाल नहीं किए गए ऑब्जेक्ट में से किसी एक को रीसाइकल करें. जब बाहरी कोड, ऑब्जेक्ट का इस्तेमाल कर लेता है, तो उसे मुख्य मेमोरी में छोड़ने के बजाय, पूल में वापस कर दिया जाता है. ऑब्जेक्ट को कोड से कभी डिरेफ़रंस (यानी मिटाया) नहीं किया जाता, इसलिए उसे ग़ैर-ज़रूरी डेटा के तौर पर नहीं हटाया जाएगा. ऑब्जेक्ट पूल का इस्तेमाल करने से, प्रोग्रामर के पास मेमोरी का कंट्रोल वापस आ जाता है. इससे परफ़ॉर्मेंस पर, गै़रबेज कलेक्टर का असर कम हो जाता है.
ऐप्लिकेशन में अलग-अलग तरह के ऑब्जेक्ट होते हैं. इसलिए, ऑब्जेक्ट पूल का सही तरीके से इस्तेमाल करने के लिए, आपको हर उस टाइप के लिए एक पूल बनाना होगा जो आपके ऐप्लिकेशन के रनटाइम के दौरान ज़्यादा बार इस्तेमाल होता है.
var newEntity = gEntityObjectPool.allocate();
newEntity.pos = {x: 215, y: 88};
//..... do some stuff with the object that we need to do
gEntityObjectPool.free(newEntity); //free the object when we're done
newEntity = null; //free this object reference
ज़्यादातर ऐप्लिकेशन के लिए, आपको नए ऑब्जेक्ट को असाइन करने की ज़रूरत नहीं पड़ेगी. अपने ऐप्लिकेशन को कई बार चलाकर, आपको यह पता चल जाएगा कि ऑब्जेक्ट की यह ऊपरी सीमा क्या है. साथ ही, अपने ऐप्लिकेशन की शुरुआत में ही ऑब्जेक्ट की संख्या को पहले से तय किया जा सकता है.
ऑब्जेक्ट को पहले से असाइन करना
अपने प्रोजेक्ट में ऑब्जेक्ट पूल करने की सुविधा लागू करने से, आपको अपने ऐप्लिकेशन के रनटाइम के दौरान ज़रूरी ऑब्जेक्ट की संख्या के लिए, सैद्धांतिक तौर पर ज़्यादा से ज़्यादा संख्या मिलेगी. अपनी साइट को अलग-अलग टेस्टिंग स्थितियों में चलाने के बाद, आपको यह पता चल सकता है कि आपको किस तरह की मेमोरी की ज़रूरत होगी. साथ ही, उस डेटा को किसी जगह पर कैटलॉग किया जा सकता है और उसका विश्लेषण करके यह समझा जा सकता है कि आपके ऐप्लिकेशन के लिए मेमोरी की ज़रूरत की ऊपरी सीमा क्या है.
इसके बाद, अपने ऐप्लिकेशन के शिपिंग वर्शन में, सभी ऑब्जेक्ट पूल को तय संख्या में पहले से भरने के लिए, शुरू करने का चरण सेट किया जा सकता है. इस कार्रवाई से, आपके ऐप्लिकेशन के सामने सभी ऑब्जेक्ट शुरू हो जाएंगे. साथ ही, इसको लागू करने के दौरान, डाइनैमिक तौर पर होने वाले ऐलोकेशन को कम कर देगा.
function init() {
//preallocate all our pools.
//Note that we keep each pool homogeneous wrt object types
gEntityObjectPool.preAllocate(256);
gDomObjectPool.preAllocate(888);
}
आपने जो रकम चुनी है उसका आपके ऐप्लिकेशन के व्यवहार से काफ़ी लेना-देना है. कभी-कभी, ज़्यादा से ज़्यादा रकम चुनना सबसे अच्छा विकल्प नहीं होता. उदाहरण के लिए, औसत अधिकतम चुनने से आपका मेमोरी फ़ुटप्रिंट कम हो सकता है. हालांकि, ऐसा उन उपयोगकर्ताओं के लिए होता है जो पावर इस्तेमाल नहीं करते.
यह कोई आसान काम नहीं है
ऐप्लिकेशन के ऐसे कई कैटगरी हैं जिनमें स्टैटिक मेमोरी के बढ़ने के पैटर्न का फ़ायदा मिल सकता है. हालांकि, Chrome के साथ काम करने वाले डेवलपर रेनाटो मैंगनी के मुताबिक, इसमें कुछ कमियां हैं.
नतीजा
वेब के लिए JavaScript के सही होने की एक वजह यह है कि यह भाषा शुरू करने में तेज़, मज़ेदार, और आसान भाषा है. इसकी मुख्य वजह, सिंटैक्स से जुड़ी पाबंदियों में कम रुकावट होती है. साथ ही, यह आपकी ओर से मेमोरी से जुड़ी समस्याओं को हैंडल करने में रुकावट डालती है. आप कोड छोड़कर दूसरे काम को छोड़ सकते हैं. हालांकि, HTML5 गेम जैसे बेहतरीन परफ़ॉर्मेंस वाले वेब ऐप्लिकेशन के लिए, जीसी अक्सर ज़रूरी फ़्रेम रेट को कम कर सकता है. इससे, असली उपयोगकर्ता को खराब अनुभव मिलता है. कुछ सावधानी से इंस्ट्रूमेंटेशन और ऑब्जेक्ट पूल का इस्तेमाल करके, अपने फ़्रेम रेट पर इस बोझ को कम किया जा सकता है. साथ ही, उस समय का इस्तेमाल ज़्यादा बेहतर चीज़ों के लिए किया जा सकता है.
सोर्स कोड
वेब पर ऑब्जेक्ट पूल के कई तरीके मौजूद हैं. इसलिए, हम आपको एक और तरीके के बारे में नहीं बताएंगे. इसके बजाय, हम आपको इन पर ले जाएंगे. इनमें से हर एक को लागू करने के तरीके अलग-अलग हैं. यह ज़रूरी है, क्योंकि हर ऐप्लिकेशन के इस्तेमाल के लिए, लागू करने की अलग-अलग ज़रूरतें हो सकती हैं.
- Gamecore.js का ऑब्जेक्ट पूल
- Beej के ऑब्जेक्ट पूल
- Emehrkay का बेहद सिंपल ऑब्जेक्ट पूल
- स्टीफ़न लैंबर्ट का गेम पर फ़ोकस करने वाला ऑब्जेक्ट पूल
- RenderEngine का objectPool सेटअप