जावास्क्रिप्ट हमारे लिए खुद सफाई क्यों करता है, लेकिन फिर भी हमें यह समझना चाहिए कि यह कैसे काम करता है?
\n\nजब आप जावास्क्रिप्ट में कोड लिखते हैं, तो आप शायद ही कभी सोचते हैं कि वेरिएबल्स के स्कोप से बाहर निकलने के बाद उनका क्या होता है। C या C++ के विपरीत, जहाँ आपको मैन्युअल रूप से free() या delete कॉल करना होता है, जावास्क्रिप्ट मेमोरी साफ करने का गंदा काम अपने ऊपर ले लेता है। यह सुविधाजनक है — जब तक कि आपका एप्लिकेशन 'लैग' करना शुरू न कर दे, गीगाबाइट्स RAM खपत न करने लगे, या अचानक Out of Memory त्रुटि के साथ क्रैश न हो जाए।
परिचित लगता है? तो चलिए समझते हैं कि वास्तव में जावास्क्रिप्ट में मेमोरी प्रबंधन कैसे काम करता है और जावास्क्रिप्ट गार्बेज कलेक्टर क्या है। स्पॉयलर: यह कोई जादू नहीं, बल्कि एक सख्त एल्गोरिदम है, जिसे कोड लिखते समय ध्यान में रखा जा सकता है (और रखना चाहिए)।
\n\nजावास्क्रिप्ट डेटा कैसे स्टोर करता है: स्टैक और हीप
\n\nगार्बेज कलेक्शन के बारे में बात करने से पहले, यह समझना ज़रूरी है कि डेटा कहाँ रहता है। जावास्क्रिप्ट में दो मुख्य स्टोरेज हैं:
\n\n- \n
- स्टैक (Stack) — तेज़, लेकिन सीमित मेमोरी। यहाँ प्रिमिटिव (नंबर, स्ट्रिंग्स, बूलियन वैल्यू) और ऑब्जेक्ट्स के संदर्भ स्टोर होते हैं। फंक्शन का हर कॉल स्टैक में एक नया फ्रेम बनाता है। \n
- हीप (Heap) — मेमोरी का एक असंरचित क्षेत्र, जहाँ ऑब्जेक्ट्स, ऐरे, फंक्शन और क्लोज़र रहते हैं। यहीं पर मेमोरी के लिए मुख्य संघर्ष होता है। \n
जब आप एक ऑब्जेक्ट बनाते हैं:
\n\nlet user = { name: 'Иван', age: 30 };\n\n\nसंदर्भ user स्टैक में जाता है, और ऑब्जेक्ट { name: 'Иван', age: 30 } हीप में जाता है। जब तक ऑब्जेक्ट पर कम से कम एक संदर्भ है, वह जीवित है। जैसे ही कोई संदर्भ नहीं बचता, ऑब्जेक्ट 'कचरा' बन जाता है।
जावास्क्रिप्ट में गार्बेज कलेक्टर: मुख्य नायक
\n\nजावास्क्रिप्ट में गार्बेज कलेक्टर (Garbage Collector, GC) एक बैकग्राउंड प्रक्रिया है, जो समय-समय पर हीप की जाँच करती है और अप्राप्य ऑब्जेक्ट्स को हटा देती है। यह सुनने में सरल लगता है, लेकिन व्यवहार में यह सफाई की गति और आपके कोड के प्रदर्शन के बीच एक जटिल समझौता है।
\n\nMark-and-Sweep एल्गोरिदम (चिह्नित करना और साफ करना)
\n\nआधुनिक इंजन (Chrome में V8, Firefox में SpiderMonkey) Mark-and-Sweep एल्गोरिदम का उपयोग करते हैं। यह दो चरणों में काम करता है:
\n\n- \n
- Mark (चिह्नित करना): GC रूट ऑब्जेक्ट्स (ग्लोबल ऑब्जेक्ट, वर्तमान फंक्शन, लोकल वेरिएबल्स) से शुरू होता है। यह रूट्स से सभी संदर्भों को ट्रैवर्स करता है और प्रत्येक प्राप्य ऑब्जेक्ट को चिह्नित करता है। \n
- Sweep (साफ करना): सभी अचिह्नित ऑब्जेक्ट्स को कचरा माना जाता है और हटा दिया जाता है। मेमोरी मुक्त हो जाती है। \n
सरल उदाहरण:
\n\nlet a = { data: 'A' };\nlet b = { data: 'B' };\na.ref = b; // a, b को संदर्भित करता है\nb.ref = a; // b, a को संदर्भित करता है\n\n// अब हमारे पास एक चक्रीय संदर्भ है\na = null; // a अब ऑब्जेक्ट की ओर इशारा नहीं करता\nb = null; // b अब ऑब्जेक्ट की ओर इशारा नहीं करता\n\n\nपहले (Mark-and-Sweep के आने से पहले) ऐसे चक्रीय संदर्भ लीक का कारण बनते थे। लेकिन आधुनिक GC समझता है: यदि दोनों ऑब्जेक्ट रूट से अप्राप्य हैं, तो वे कचरा हैं, भले ही वे एक-दूसरे को संदर्भित करते हों।
\n\nऑब्जेक्ट्स की पीढ़ियाँ: युवा और वृद्ध
\n\nअधिकांश ऑब्जेक्ट्स लंबे समय तक जीवित नहीं रहते। उदाहरण के लिए, फंक्शन के अंदर अस्थायी वेरिएबल्स। इसलिए V8 हीप को पीढ़ियों में विभाजित करता है:
\n\n- \n
- Young Generation (युवा पीढ़ी): नए ऑब्जेक्ट्स। यहाँ सफाई बार-बार और तेज़ी से होती है (Scavenge — एक एल्गोरिदम जो जीवित ऑब्जेक्ट्स को दूसरे क्षेत्र में कॉपी करता है)। \n
- Old Generation (वृद्ध पीढ़ी): वे ऑब्जेक्ट्स जो कई सफाई चक्रों से बच गए हैं। इनकी जाँच कम बार होती है, लेकिन प्रक्रिया में अधिक समय लगता है। \n
यह दृष्टिकोण पूरे हीप की पूर्ण जाँच के दौरान पेज को 'फ्रीज़' नहीं होने देता।
\n\nजावास्क्रिप्ट में मेमोरी लीक: उन्हें कैसे होने दें (और नहीं देना चाहिए)
\n\nस्मार्ट गार्बेज कलेक्टर के साथ भी, ऐसी स्थिति बनाई जा सकती है जहाँ मेमोरी मुक्त नहीं होती। यहाँ मेमोरी लीक के सबसे सामान्य कारण हैं