कचरा। रंग ()

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

सिद्धांत रूप में, यह अच्छा लगता है। व्यवहार में, उपयोगकर्ता YouTube से 20 टैब खोलता है, सामाजिक नेटवर्क, कुछ पढ़ता है, काम करता है, ब्राउज़र मेमोरी को खाता है, जैसे हथौड़ा एच 2 - गैसोलीन। कचरा संग्रहकर्ता, मोप के साथ इस राक्षस की तरह, पूरी स्मृति से चलता है और भ्रम को जोड़ता है, सब कुछ धीमा हो जाता है और दुर्घटनाग्रस्त हो जाता है।



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

हम हर दिन कचरा संग्राहक (घर में नहीं - सामने के अंत में विकास) का उपयोग करते हैं, लेकिन हम वास्तव में इस बारे में नहीं सोचते हैं कि यह क्या है, यह हमारे लिए क्या खर्च करता है और इसके क्या अवसर और सीमाएं हैं।

यदि कचरा संग्रह वास्तव में जावास्क्रिप्ट में काम करता है, तो अधिकांश एनपीएम मॉड्यूल स्थापना के तुरंत बाद खुद को हटा देगा।

लेकिन जब तक ऐसा नहीं है, और हम इस बारे में बात करेंगे कि क्या है - अनावश्यक वस्तुओं को इकट्ठा करने के बारे में।


वक्ता के बारे में : आंद्रेई रोन्को Yandex.Map एपीआई को विकसित कर रहा है , अब छह साल से सीमांत में है, अपने खुद के उच्च सार बनाने और अन्य लोगों से जमीन पर उतरने के लिए प्यार करता है।

आपको कचरा संग्रहण की आवश्यकता क्यों है?


Yandex.Maps के उदाहरण पर विचार करें। Yandex.Maps एक विशाल और जटिल सेवा है जो मल्टीमीडिया वाले को छोड़कर JS और लगभग सभी मौजूदा ब्राउज़र APIs का उपयोग करता है, और औसत सत्र का समय 5-10 मिनट है। जावास्क्रिप्ट की प्रचुरता कई वस्तुओं का निर्माण करती है। मानचित्र को खींचना, संगठनों को जोड़ना, खोज परिणाम और हर दूसरे होने वाली कई अन्य घटनाओं में वस्तुओं का हिमस्खलन होता है। इस प्रतिक्रिया में जोड़ें और वस्तुएं और भी अधिक हो जाती हैं।

हालांकि, जेएस-ऑब्जेक्ट्स नक्शे पर केवल 30-40 एमबी पर कब्जा कर लेते हैं। लंबे Yandex.Maps सत्र और नई वस्तुओं के निरंतर आवंटन के लिए, यह पर्याप्त नहीं है।

वस्तुओं की कम मात्रा का कारण यह है कि वे कचरा कलेक्टर द्वारा सफलतापूर्वक एकत्र किए जाते हैं और मेमोरी का पुन: उपयोग किया जाता है।

आज हम चार तरफ से कचरा संग्रहण के बारे में बात करेंगे:

  • सिद्धांत। आइए एक ही भाषा बोलने और एक दूसरे को समझने के लिए उसके साथ शुरुआत करें।
  • हर्ष वास्तविकता। अंततः, कंप्यूटर मशीन कोड निष्पादित करता है जिसमें हमारे लिए परिचित सभी सार नहीं होते हैं। आइए यह जानने की कोशिश करें कि कचरा संग्रह निम्न स्तर पर कैसे काम करता है।
  • ब्राउज़र की वास्तविकता। आइए देखें कि आधुनिक इंजन और ब्राउज़रों में कचरा संग्रह कैसे लागू किया जाता है, और इससे हम क्या निष्कर्ष निकाल सकते हैं।
  • रोजमर्रा की जिंदगी - चलो रोजमर्रा की जिंदगी में प्राप्त ज्ञान के व्यावहारिक अनुप्रयोग के बारे में बात करते हैं।

हम सभी बयानों का समर्थन करते हैं कि आप कैसे कर सकते हैं और आपको इसे करने की आवश्यकता नहीं है।

यह सब क्यों जानते हैं?


कचरा संग्रह हमारे लिए एक अदृश्य चीज है, हालांकि, यह जानना कि यह आपकी व्यवस्था कैसे होगी:

  • आपके द्वारा उपयोग किए जा रहे उपकरण का एक विचार है, जो आपके काम में उपयोगी है।
  • समझें कि पहले से जारी किए गए एप्लिकेशन को कहां ऑप्टिमाइज़ करना है और भविष्य के लोगों को कैसे डिज़ाइन करना है ताकि वे बेहतर और तेज़ काम करें।
  • जानते हैं कि सामान्य गलतियाँ कैसे न करें और बेकार और हानिकारक "अनुकूलन" पर संसाधनों को बर्बाद करना बंद करें।

सिद्धांत


जोएल स्पोल्स्की ने एक बार कहा था:

सभी गैर-तुच्छ सार लीक हैं।

कचरा इकट्ठा करने वाला एक बड़ा गैर-तुच्छ अमूर्त है जो चारों तरफ से गढ़ा हुआ है। सौभाग्य से, यह बहुत कम ही बहता है।

आइए एक सिद्धांत के साथ शुरू करें, लेकिन बिना उबाऊ परिभाषा के। आइए एक उदाहरण के रूप में सरल कोड का उपयोग करके कलेक्टर के काम का विश्लेषण करें:

window.Foo = class Foo { constructor() { this.x = { y: 'y' }; } work(name) { let z = 'z'; return function () { console.log(name, this.xy, z); this.x = null; }.bind(this); } }; 

  • कोड में एक वर्ग है।
  • क्लास में कंस्ट्रक्टर है
  • कार्य विधि संबंधित फ़ंक्शन देता है।
  • फ़ंक्शन के अंदर, यह और क्लोजर से कुछ चर का उपयोग किया जाता है।

आइए देखें कि अगर हम इसे इस तरह से चलाते हैं तो यह कोड कैसे व्यवहार करेगा:

 var foo = new Foo(); //C   window.worker = foo.work('Brendan Eich'); //     bind,   window.foo = null; //   window.Foo = null; //  ,   -  window.worker(); window.worker = null; //   ,   

आइए कोड और उसके घटकों का अधिक विस्तार से विश्लेषण करें और कक्षा से शुरू करें।

वर्ग घोषणा




हम यह मान सकते हैं कि ईसीएमएस्क्रिप्ट 2015 में कक्षाएं केवल कार्यों के लिए सिंटैक्टिक चीनी हैं। सभी कार्यों में है:

  • समारोह। [[प्रोटोटाइप]] फ़ंक्शन का वास्तविक प्रोटोटाइप है।
  • Foo.prototype हौसले से बनाई गई वस्तुओं के लिए एक प्रोटोटाइप है।
  • Foo.prototyp में कंस्ट्रक्टर फ़ील्ड के माध्यम से कंस्ट्रक्टर के लिए वापस लिंक है। यह एक ऑब्जेक्ट है, इसलिए यह Object.prototyp से इनहेरिट करता है
  • कार्य विधि एक अलग कार्य है जिसमें एक लिंक है, जो कंस्ट्रक्टर के समान है, क्योंकि वे दोनों केवल कार्य हैं। वह एक प्रोटोटाइप भी सेट कर सकता है और इसे नए माध्यम से कह सकता है, लेकिन शायद ही कोई इस व्यवहार का उपयोग करता है।

प्रोटोटाइप सर्किट पर बहुत अधिक जगह लेते हैं, इसलिए चलो याद रखें कि वे हैं, लेकिन उन्हें सरलता के लिए हटा देगा।

एक क्लास ऑब्जेक्ट बनाना




  • हमने अपनी कक्षा को विंडो में रखा, क्योंकि कक्षाएं डिफ़ॉल्ट रूप से वहां नहीं मिलती हैं।
  • एक क्लास ऑब्जेक्ट बनाएं।
  • ऑब्जेक्ट बनाना स्वचालित रूप से Foo.prototype में क्लास ऑब्जेक्ट के प्रोटोटाइप को उजागर करता है। इसलिए, जब आप किसी ऑब्जेक्ट पर कार्य विधि को कॉल करने का प्रयास करते हैं, तो यह पता चलेगा कि यह किस तरह का काम है।
  • हमारा कंस्ट्रक्टर ऑब्जेक्ट के साथ स्ट्रिंग से ऑब्जेक्ट में फ़ील्ड x बनाता है।

यहाँ क्या हुआ:



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



पर्यावरण LexicalEnvironment के लिए एक संदर्भ संग्रहीत करता है। यह एक जटिल वस्तु है, जो स्लाइड से अधिक जटिल है; यह एक फंक्शन से एक्सेस की जा सकने वाली सभी चीज़ों को लिंक करती है। उदाहरण के लिए, विंडो, फू, नाम और जेड। यह उन लिंक को भी संग्रहीत करता है जो आप स्पष्ट रूप से उपयोग नहीं कर रहे हैं। उदाहरण के लिए, आप eval का उपयोग कर सकते हैं और गलती से अप्रयुक्त वस्तुओं का उपयोग कर सकते हैं, लेकिन JS को नहीं तोड़ना चाहिए।

इसलिए, हमने सभी वस्तुओं का निर्माण किया और अब हम सब कुछ नष्ट कर देंगे।

ऑब्जेक्ट का लिंक हटाएं


आइए ऑब्जेक्ट के लिंक को हटाकर शुरू करें, आरेख में यह लिंक लाल रंग में हाइलाइट किया गया है।



हम हटाते हैं और कुछ भी नहीं होता है, क्योंकि विंडो से ऑब्जेक्ट तक बाध्य फ़ंक्शन फ़ंक्शन के माध्यम से एक रास्ता है।



यह हमें एक सामान्य गलती की ओर धकेलता है।

सामान्य गलती - सदस्यता भूल गए


 externalElement.addEventListener('click', () => { if (this.shouldDoSomethingOnClick) { this.doSomething(); } }) 

जब आप सदस्यता लेते हैं: इसका उपयोग , स्पष्ट रूप से बाइंड या तीर फ़ंक्शन के माध्यम से; बंद में कुछ का उपयोग करें। फिर आप सदस्यता समाप्त करना भूल जाते हैं, और आपके जीवनकाल या सर्किट में जो कुछ भी होता है वह सदस्यता के जीवनकाल के समान हो जाता है। उदाहरण के लिए, यदि यह एक DOM तत्व है जिसे आप स्पर्श नहीं करते हैं, तो यह पृष्ठ के जीवन के अंत तक सबसे अधिक संभावना है।

इन समस्याओं को हल करने के लिए:

  • सदस्यता समाप्त।
  • सदस्यता के जीवनकाल के बारे में सोचें, और जो इसका मालिक है।
  • यदि किसी कारण से आप सदस्यता समाप्त नहीं कर सकते हैं, तो लिंक (जो भी = अशक्त) को शून्य करें, या ऑब्जेक्ट के सभी क्षेत्रों को साफ करें। यदि आपकी वस्तु लीक होती है, तो यह छोटी होगी और यह कोई दया नहीं है।
  • WeakMap का उपयोग करें, शायद यह कुछ स्थितियों में मदद करेगा।

वर्ग संदर्भ हटाएं


आगे बढ़ें और वर्ग-हाइलाइट किए गए लाल लिंक को हटाने का प्रयास करें।



हम लिंक को हटा देते हैं और हमारे लिए कुछ भी नहीं बदलता है। कारण यह है कि क्लास बाउंड्स के माध्यम से सुलभ है, जिसमें प्रोटोटाइप के लिए एक लिंक है, और प्रोटोटाइप में कंस्ट्रक्टर के लिए एक लिंक है।

ठेठ गलती बेकार काम


इन सभी प्रदर्शनों की आवश्यकता क्यों है? क्योंकि समस्या का एक दूसरा पक्ष है जब लोग लिंक को बहुत अधिक शाब्दिक रूप से निरस्त करने की सलाह लेते हैं और सामान्य रूप से सब कुछ समाप्त कर देते हैं।

 destroy() { this._x = null; this._y = null; //  10 this._foobar = null } 

यह एक बहुत बेकार काम है। यदि ऑब्जेक्ट में केवल अन्य ऑब्जेक्ट्स के संदर्भ होते हैं और वहां कोई संसाधन नहीं हैं, तो किसी भी विनाश () की आवश्यकता नहीं है। यह ऑब्जेक्ट के संदर्भ को खोने के लिए पर्याप्त है, और वह अपने दम पर मर जाएगा।

कोई सार्वभौमिक सलाह नहीं है। जब यह आवश्यक हो, तो nullify, और जब नहीं, तो nullify न करें। ज़ीरोइंग एक गलती नहीं है, लेकिन बस बेकार काम है।

आगे बढ़ो। बाध्य फ़ंक्शन विधि को कॉल करें और यह लिंक को [ऑब्जेक्ट फू] से [ऑब्जेक्ट ऑब्जेक्ट] तक हटा देगा। यह इस तथ्य की ओर ले जाएगा कि एक नीली आयत में अलग-अलग होने वाली वस्तुएं आरेख में दिखाई देती हैं।



ये वस्तुएं जेएस कचरा हैं। वह महान जा रहा है। हालांकि, कचरा है जिसे एकत्र नहीं किया जा सकता है।

कचरा जो नहीं जा रहा है


कई ब्राउज़र एपीआई में, आप एक ऑब्जेक्ट बना और नष्ट कर सकते हैं। यदि ऑब्जेक्ट नष्ट नहीं होता है, तो कोई भी कलेक्टर इसे इकट्ठा नहीं कर सकता है।

जोड़ी कार्यों को बनाने / हटाने के साथ वस्तुएं:

  • createObjectURL (), revokeObjectURL ();
  • WebGL: बनाएं / हटाएं प्रोग्राम / शेडर / बफर / टेक्सचर / आदि;
  • ImageBitmap.close ();
  • indexDb.close ()।

उदाहरण के लिए, यदि आप 200 एमबी वीडियो से ऑब्जेक्ट को हटाना भूल जाते हैं, तो स्मृति में ये 200 एमबी पेज के जीवन के अंत तक और इससे भी लंबे समय तक रहेंगे, क्योंकि टैब के बीच डेटा एक्सचेंज है। इसी तरह WebGL, indexDb और अन्य ब्राउज़र API में समान संसाधनों के साथ।

सौभाग्य से, हमारे उदाहरण में, नीले आयत में सिर्फ जावास्क्रिप्ट ऑब्जेक्ट शामिल हैं, इसलिए यह केवल कचरा है जिसे हटाया जा सकता है।

अगला चरण बाएं से दाएं अंतिम लिंक को साफ़ करना है। यह हमारे द्वारा प्राप्त विधि, एक संबंधित फ़ंक्शन का संदर्भ है।



इसके हटाने के बाद, हमारे पास बाएं और दाएं कोई लिंक नहीं होगा? वास्तव में, अभी भी बंद से लिंक हैं।



यह महत्वपूर्ण है कि बाएं से दाएं कोई लिंक नहीं हैं, इसलिए खिड़की को छोड़कर सब कुछ कचरा है और यह मर जाएगा।

महत्वपूर्ण नोट : कचरे में परिपत्र संदर्भ होते हैं, अर्थात ऑब्जेक्ट जो एक दूसरे को संदर्भित करते हैं। ऐसे लिंक की उपस्थिति कुछ भी प्रभावित नहीं करती है, क्योंकि कचरा कलेक्टर व्यक्तिगत वस्तुओं को इकट्ठा नहीं करता है, लेकिन पूरे कचरे को।



हमने उदाहरणों को देखा और अब एक सहज स्तर पर हम समझते हैं कि कचरा क्या है, लेकिन आइए अवधारणा की पूरी परिभाषा दें।

कचरा सब कुछ है जो एक जीवित वस्तु नहीं है।

सब कुछ बहुत स्पष्ट हो गया। लेकिन एक जीवित वस्तु क्या है?

एक जीवित वस्तु एक ऐसी वस्तु है जिसे मूल वस्तु से लिंक द्वारा पहुँचा जा सकता है।

दो नई अवधारणाएँ दिखाई देती हैं: "लिंक का अनुसरण करें" और "मूल वस्तु"। एक मूल वस्तु जिसे हम पहले से जानते हैं वह है विंडो, तो चलिए लिंक से शुरू करते हैं।

लिंक्स का पालन करने का क्या मतलब है?


ऐसी कई वस्तुएं हैं जो एक दूसरे से संबंधित हैं और एक दूसरे का संदर्भ देती हैं। हम रूट ऑब्जेक्ट के साथ शुरू करते हुए, उनके साथ तरंग करेंगे।

हम पहले चरण को इनिशियलाइज़ करते हैं, और फिर निम्न एल्गोरिथम के अनुसार आगे बढ़ते हैं: मान लें कि लहर के शिखर पर मौजूद हर एक वस्तु जीवित है और देखें कि वे क्या संदर्भित करते हैं।



हम पहला कदम इनिशियलाइज़ करते हैं। फिर हम निम्नलिखित एल्गोरिथ्म के अनुसार कार्य करेंगे: मान लें कि लहर की शिखा पर पीली हर चीज जीवित वस्तुएं हैं और आइए देखें कि वे किस चीज का उल्लेख करते हैं।

वे क्या संदर्भित करते हैं, हम लहर की एक नई शिखा बनाएंगे:



समाप्त और शुरू:

  • हम पुनर्जीवित हो रहे हैं।
  • हम देखें कि वे क्या संदर्भित करते हैं।
  • एक नई लहर शिखा, चेतन वस्तुओं बनाएँ।
  • हम देखें कि वे क्या संदर्भित करते हैं।



यह देखते हुए कि एक तीर पहले से ही जीवित वस्तु की ओर इशारा करता है, हम बस कुछ नहीं करते हैं। एल्गोरिथ्म के अनुसार आगे, जब तक कि वस्तुओं के चारों ओर जाने के लिए बाहर नहीं चला जाता है। तब हम कहते हैं कि हमने सभी जीवित वस्तुओं को पाया, और बाकी सब कचरा है।



इस प्रक्रिया को अंकन कहा जाता है।

जड़ वस्तु का क्या अर्थ है?



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

असेंबली कभी भी हो सकती है। हर बार ब्रेसिज़ या फ़ंक्शन दिखाई देते हैं, एक नई वस्तु बनाई जाती है। पर्याप्त मेमोरी नहीं हो सकती है, और कलेक्टर मुफ्त में तलाश करेंगे:

 function foo (a, b, c) { function bar (x, y, z) { const x = {}; // nomem, run gc D: // … } while (whatever()) bar(); } 

इस स्थिति में, रूट ऑब्जेक्ट कॉल स्टैक पर सब कुछ होगा। यदि आप, उदाहरण के लिए, एक्स के साथ लाइन पर रुकें और वाई को देखें, तो आपका एप्लिकेशन क्रैश हो जाएगा। जेएस हमें इस तरह की तुच्छता की अनुमति नहीं देता है, इसलिए आप वाई से एक वस्तु को नहीं हटा सकते हैं।

यदि पिछला हिस्सा जटिल लग रहा था, तो यह और भी मुश्किल होगा।

हर्ष वास्तविकता


आइए मशीनों की दुनिया के बारे में बात करते हैं जिसमें हम लोहे के साथ सौदा करते हैं, भौतिक मीडिया के साथ।

मेमोरी एक बड़ा सरणी है जिसमें सिर्फ संख्याएं झूठ होती हैं, उदाहरण के लिए: नई Uint32Array (16 * 2 ** 30)।

आइए स्मृति में ऑब्जेक्ट बनाएं और उन्हें बाएं से दाएं जोड़ें। हम एक, दूसरा, तीसरा बनाते हैं - वे सभी विभिन्न आकारों के हैं। हमने रास्ते में लिंक लगाए।


सातवीं वस्तु में, स्थान समाप्त हो गया है, क्योंकि हमारे पास 2 निःशुल्क वर्ग हैं, लेकिन हमें 5 की आवश्यकता है।

यहां क्या किया जा सकता है? पहला विकल्प दुर्घटनाग्रस्त होना है। 2018 में यार्ड में, सभी के पास नवीनतम मैकबुक और 16 जीबी रैम है। कोई स्थिति नहीं है जब कोई स्मृति नहीं है!

हालांकि, चीजों को अपने पाठ्यक्रम को चलाने देना एक बुरा विचार है, क्योंकि वेब पर यह एक समान स्क्रीन की ओर जाता है:



यह वह व्यवहार नहीं है जो हम कार्यक्रम से चाहते हैं, लेकिन सामान्य तौर पर यह मान्य है। कलेक्टरों की एक श्रेणी है जिसे नो-ऑप कहा जाता है।

नो-ऑप कलेक्टर


पेशेवरों:

  • कलेक्टर बहुत सरल है।
  • बस कोई कचरा संग्रह नहीं है।
  • मेमोरी के बारे में लिखने या सोचने की जरूरत नहीं है।

विपक्ष:

  • सब कुछ इतना गिर जाता है कि फिर कभी उगता नहीं है।

फ्रंटेंड के लिए, नो-ऑप कलेक्टर अप्रासंगिक है, लेकिन बैकएंड पर उपयोग किया जाता है। उदाहरण के लिए, बैलेन्सर के पीछे कई सर्वर होते हैं, एप्लिकेशन को 32 जीबी रैम दी जाती है और फिर इसे पूरी तरह से मार दिया जाता है। मेमोरी कम होने पर इसे फिर से शुरू करके केवल सरल और प्रदर्शन में सुधार किया जाता है।

वेब पर यह असंभव है और आपको इसे साफ करना होगा।

कचरा खोजो और हटाओ


हम कूड़ेदान से सफाई शुरू करते हैं। हम पहले से ही जानते हैं कि यह कैसे करना है। कचरा - पिछली योजना में सी और एफ की वस्तुओं, क्योंकि आप उन्हें जड़ वस्तु से तीर तक नहीं पहुंचा सकते हैं।

हम इस कचरे को लेते हैं, इसे कचरा प्रेमी को खिलाते हैं और आपका काम हो गया।



सफाई के बाद, समस्या हल नहीं होती है, क्योंकि मेमोरी में छेद बने रहते हैं। कृपया ध्यान दें कि 7 निशुल्क वर्ग हैं, लेकिन उनमें से 5 अभी भी हम आवंटित नहीं कर सकते हैं। विखंडन हुआ और विधानसभा समाप्त हो गई। छेद वाले इस तरह के एक एल्गोरिथ्म को मार्क और स्वीप कहा जाता है।

निशान और झाडू


पेशेवरों:

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

विपक्ष:

  • मुक्त स्थान की खोज के लिए जटिल तर्क की आवश्यकता होती है, क्योंकि जब मेमोरी में बहुत सारे छेद होते हैं, तो आपको यह समझने के लिए प्रत्येक में एक ऑब्जेक्ट पर प्रयास करना होगा कि वह फिट बैठता है या नहीं।
  • सुगंध स्मृति। ऐसी स्थिति हो सकती है कि मुफ्त 200 एमबी के साथ मेमोरी को छोटे टुकड़ों में विभाजित किया जाता है और, जैसा कि ऊपर दिए गए उदाहरण में, ऑब्जेक्ट के लिए मेमोरी का कोई ठोस टुकड़ा नहीं है।

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

ऐसा एक एल्गोरिथ्म है और इसे मार्क और कॉम्पैक्ट कहा जाता है।

निशान और कॉम्पैक्ट


पेशेवरों:

  • डीफ्रैगमेंट मेमोरी।
  • यह जीवित वस्तुओं की संख्या के अनुपात में काम करता है, जिसका अर्थ है कि इसका उपयोग तब किया जा सकता है जब व्यावहारिक रूप से कोई मलबे न हो।

विपक्ष:

  • कार्य और कार्यान्वयन में कठिनाई।
  • वस्तुओं को ले जाता है। हमने ऑब्जेक्ट को स्थानांतरित किया, इसे कॉपी किया, अब यह एक अलग जगह पर है और पूरा ऑपरेशन काफी महंगा है।
  • इसे पूरे मेमोरी में 2-3 पास की आवश्यकता होती है, कार्यान्वयन के आधार पर - एल्गोरिथ्म धीमा है।

यहाँ हम एक और विचार पर आते हैं।

कचरा संग्रह मुफ्त नहीं है


उच्च प्रदर्शन APIs जैसे WebGL, WebAudio और WebGPU में, जो अभी भी विकास में है, वस्तुओं को अलग-अलग चरणों में बनाया और हटाया जाता है। इन विशिष्टताओं को लिखा जाता है ताकि कचरा संग्रहण प्रक्रिया में न हो। इसके अलावा, वहाँ भी वादा नहीं है, लेकिन पुल () - आप बस प्रत्येक फ्रेम से पूछते हैं: "कुछ हुआ या नहीं?"।

अर्धकुंभ उर्फ ​​लिस्प 2


एक और कलेक्टर है जिसके बारे में मैं बात करना चाहता हूं। क्या होगा यदि आप स्मृति को मुक्त नहीं करते हैं, लेकिन सभी जीवित वस्तुओं को कहीं दूसरी जगह कॉपी करें।

आइए मूल वस्तु "जैसा है" को कॉपी करने का प्रयास करें, जो कहीं न कहीं संदर्भित करता है।



और फिर हर कोई।



स्मृति में कोई मलबे या छेद नहीं हैं। सब कुछ ठीक लग रहा है, लेकिन दो समस्याएं उत्पन्न होती हैं:

  • डुप्लिकेट ऑब्जेक्ट - हमारे पास दो ग्रीन ऑब्जेक्ट और दो ब्लू हैं। कौन सा उपयोग करना है?
  • नई वस्तुओं के लिंक पुरानी वस्तुओं की ओर ले जाते हैं, न कि एक दूसरे के।

लिंक के साथ, सब कुछ एक विशेष एल्गोरिथम "जादू" की मदद से हल किया गया है, और हम नीचे सब कुछ हटाकर वस्तुओं के दोहराव से सामना कर सकते हैं।


नतीजतन, हमारे पास खाली जगह है, और ऊपर के सामान्य क्रम में केवल जीवित वस्तुएं हैं। इस एल्गोरिथ्म को सेमीस्पैस , लिस्प 2 या बस "कॉपी कलेक्टर" कहा जाता है।

पेशेवरों:

  • डीफ्रैगमेंट मेमोरी।
  • सरल।
  • एक बाईपास चरण के साथ जोड़ा जा सकता है।
  • यह समय के साथ जीवित वस्तुओं की संख्या के अनुपात में काम करता है।
  • कूड़े का ढेर लगने पर अच्छी तरह से काम करता है। यदि आपके पास 2 जीबी मेमोरी है और इसमें 3 ऑब्जेक्ट हैं, तो आप केवल 3 ऑब्जेक्ट को बायपास करेंगे, और शेष 2 जीबी चला गया लगता है।

विपक्ष:

  • डबल मेमोरी खपत। आप आवश्यकता से 2 गुना अधिक मेमोरी का उपयोग करते हैं।
  • मूविंग ऑब्जेक्ट्स भी बहुत सस्ता ऑपरेशन नहीं है।

नोट: कचरा संग्रहकर्ता वस्तुओं को स्थानांतरित कर सकते हैं।

वेब पर, यह अप्रासंगिक है, लेकिन Node.js पर बहुत अधिक है। यदि आप C ++ में एक्सटेंशन लिखते हैं, तो भाषा को इस सब के बारे में नहीं पता है, इसलिए वहां डबल लिंक हैं जिन्हें हैंडल कहा जाता है और कुछ इस तरह दिखता है: v8 :: स्थानीय <v8 :: स्ट्रिंग>।

इसलिए, यदि आप Node.js के लिए प्लगइन्स लिखने जा रहे हैं तो जानकारी काम आएगी।

हम तालिका में उनके पेशेवरों और विपक्षों के साथ विभिन्न एल्गोरिदम को संक्षेप में प्रस्तुत करते हैं। इसमें एडेन एल्गोरिथ्म भी है, लेकिन इसके बारे में बाद में।



मैं वास्तव में विपक्ष के बिना एक एल्गोरिथ्म चाहता हूं, लेकिन यह नहीं है। इसलिए, हम सभी दुनिया में सबसे अच्छा लेते हैं: हम एक ही समय में कई एल्गोरिदम का उपयोग करते हैं। स्मृति के एक टुकड़े में, हम एक एल्गोरिथ्म के साथ कचरा इकट्ठा करते हैं, और दूसरे में दूसरे एल्गोरिथ्म के साथ।

ऐसी स्थिति में एल्गोरिथ्म की प्रभावशीलता को कैसे समझा जाए?

हम 60 के दशक से स्मार्ट पतियों के ज्ञान का उपयोग कर सकते हैं जिन्होंने सभी कार्यक्रमों को देखा और महसूस किया:

कमजोर पीढ़ी की परिकल्पना: अधिकांश वस्तुएं युवा मर जाती हैं।

ये वे कहना चाहते थे कि सभी कार्यक्रम केवल कचरा पैदा करते हैं। ज्ञान का उपयोग करने के प्रयास में, हम आने वाली पीढ़ियों को "असेंबली" कहते हैं।

पीढ़ीगत सभा


हम स्मृति के दो टुकड़े बनाते हैं जो किसी भी तरह से जुड़े नहीं हैं: बाईं ओर ईडन है, और दाईं ओर मार्क और स्वीप धीमा है। ईडन में हम ऑब्जेक्ट बनाते हैं। बहुत सारी वस्तुएं।



जब ईडन कहता है कि यह पूर्ण है, हम इसमें कचरा संग्रह शुरू करते हैं। हम जीवित वस्तुओं को ढूंढते हैं और उन्हें दूसरे कलेक्टर में कॉपी करते हैं।



ईडन खुद पूरी तरह से साफ हो गया है, और हम इसमें वस्तुओं को जोड़ सकते हैं।



पीढ़ियों की परिकल्पना पर भरोसा करते हुए, हमने तय किया कि वस्तुओं सी, जी, मैं सबसे अधिक समय तक जीवित रहूंगा, और हम उन्हें कम बार जांच सकते हैं। इस परिकल्पना को जानने के बाद, आप कलेक्टर को धोखा देने वाले कार्यक्रम लिख सकते हैं। यह किया जा सकता है, लेकिन मैं आपको सलाह नहीं देता, क्योंकि यह लगभग हमेशा अवांछनीय प्रभाव पैदा करेगा। यदि आप लंबे समय तक रहने वाला कचरा बनाते हैं, तो कलेक्टर यह मानना ​​शुरू कर देगा कि इसे एकत्र करने की आवश्यकता नहीं है।

धोखा देने का एक उत्कृष्ट उदाहरण LRU-cache है। एक वस्तु लंबे समय तक कैश में रहती है, कलेक्टर इसे देखता है और मानता है कि यह अभी तक इसे इकट्ठा नहीं करेगा, क्योंकि ऑब्जेक्ट बहुत लंबे समय तक जीवित रहेगा। फिर एक नई वस्तु कैश में मिलती है, और एक बड़े पुराने को इससे बाहर धकेल दिया जाता है और इस बड़ी वस्तु को तुरंत इकट्ठा करना संभव नहीं है।

अब कैसे एकत्र करें, हम जानते हैं। कब इकट्ठा करना है, इसके बारे में बात करें।

कब जमा करें?


सबसे आसान विकल्प यह है कि जब हम सब कुछ रोकते हैं , तो बिल्ड शुरू करें, और फिर जेएस काम फिर से शुरू करें।



आधुनिक कंप्यूटरों में, निष्पादन के एक से अधिक धागे। वेब पर, यह वेब वर्कर्स से परिचित है। विधानसभा प्रक्रिया को क्यों नहीं लिया और समांतर बनाया जाए । एक ही समय में कई छोटे ऑपरेशन करना एक से एक बड़े तेज होंगे।



एक अन्य विचार ध्यान से वर्तमान स्थिति का एक स्नैपशॉट बनाने के लिए है, और जेएस के साथ समानांतर में निर्माण करना है



यदि यह आपकी रुचि है, तो मैं आपको पढ़ने की सलाह देता हूं:

  • एकमात्र और मुख्य विधानसभा पुस्तक, कचरा संग्रह पुस्तिका।
  • एक सार्वभौमिक संसाधन के रूप में विकिपीडिया
  • वेबसाइट memorymanagement.org
  • अलेक्जेंडर शेपलेव की रिपोर्ट और लेख। वह जावा के बारे में बात करता है, लेकिन कचरे के संदर्भ में, जावा और वी 8 लगभग एक ही काम करते हैं।

ब्राउज़र की वास्तविकता


आइए आगे बढ़ते हैं कि ब्राउज़र उन सभी चीजों का उपयोग कैसे करता है जिनके बारे में हमने बात की है।

IoT इंजन


आइए ब्राउज़रों के साथ शुरू न करें, लेकिन इंटरनेट ऑफ थिंग्स इंजनों के साथ: जेरीस्क्रिप्ट और डुकटेप। वे Mark'n'sweep का उपयोग करते हैं और दुनिया के एल्गोरिदम को रोकते हैं।

IoT इंजन माइक्रोकंट्रोलर पर काम करते हैं, जिसका अर्थ है: भाषा धीमी है; दूसरा लटका; विखंडन; और प्रकाश व्यवस्था के साथ एक चायदानी के लिए यह सब :)

यदि आप जावास्क्रिप्ट में इंटरनेट ऑफ थिंग्स लिखते हैं, तो हमें टिप्पणियों में बताएं? क्या कोई बात है

हम केवल IoT इंजन छोड़ेंगे, हम इसमें रुचि रखते हैं:

  • वी 8।
  • Spidermonkey। वास्तव में, उसके पास लोगो नहीं है। घर का बना लोगो :)
  • WebKit द्वारा इस्तेमाल किया गया JavaScriptCore।
  • चक्रकोर जिसका उपयोग एज में किया जाता है।



सभी इंजन लगभग समान हैं, इसलिए हम सबसे प्रसिद्ध के रूप में वी 8 के बारे में बात करेंगे।

वी 8


  • लगभग सभी सर्वर-साइड जावास्क्रिप्ट, क्योंकि यह Node.js.
  • क्लाइंट-साइड जावास्क्रिप्ट का लगभग 80%।
  • सबसे मिलनसार डेवलपर्स, बहुत सारी जानकारी और अच्छे स्रोत कोड हैं जिन्हें पढ़ना आसान है।

V8 जेनरेशन असेंबली का उपयोग करता है।


अंतर केवल इतना है कि हमारे पास दो संग्राहक थे, और अब तीन हैं:

  • ईडन में एक ऑब्जेक्ट बनाया जाता है।
  • ईडन के कुछ बिंदु पर, बहुत अधिक कचरा है और वस्तु को सेमीस्प्रेस में स्थानांतरित कर दिया गया है।
  • ऑब्जेक्ट युवा है और जब कलेक्टर को पता चलता है कि यह बहुत पुराना है और उबाऊ है, तो वह इसे मार्क और स्वीप में फेंक देता है, जिसमें कचरा संग्रह बेहद दुर्लभ है।

आप स्पष्ट रूप से देख सकते हैं कि यह मेमोरी ट्रेस पर कैसा दिखता है।



छोटी तरंगों वाली कई बड़ी तरंगें ध्यान देने योग्य हैं। छोटे वाले छोटी विधानसभाएं हैं, और बड़े लोग प्रमुख हैं।

उत्पत्ति संबंधी परिकल्पना के अनुसार, हमारे अस्तित्व का अर्थ कचरा उत्पन्न करना है, इसलिए अगली गलती कचरा पैदा करने का डर है।

कचरा तब बनाया जा सकता है जब वह वास्तव में कचरा हो। , , , .

mark


V8 .


Stop the world, , JS, .

?


1 3%, .

3% = 1/33 GameDev. GameDev 3% 1 , . GameDev .

 const pool = [new Bullet(), new Bullet(), /* ... */]; function getFromPool() { const bullet = pool.find(x => !x.inUse); bullet.isUse = true; return bullet; } function returnToPool(bullet) { bullet.inUse = false; } // Frame const bullet = getFromPool(); // ... returnToPool(bullet); 

, , 10 000 .

— . , . , .

: Chromium


, , , Chromium.

 > performance.memory MemoryInfo { totalJSHeapSize: 10000000, usedJSHeapSize: 10000000, jsHeapSizeLimit: 2330000000 } 

Chromium performance.memory , , Chromium .

: Chromium 2 JavaScript.

, .

: Node


Node.js process.memoryUsage , .

 > process.memoryUsage() { rss: 22839296, heapTotal: 10207232, heapUsed: 5967968, external: 12829 } 

, - , . . .



— , . proposal , .

Node.js, c node-weak , , .

 let cached = new WeakRef(myJson); // 2   let json = cached.deref(); if (!json) { json = await fetchAgain(); } 

, , - JS. , , , .

WebAssembly , . , , , .

: v8.dev JS.


?



DevTools : Performance Memory . Chromium, , Firefox Safari .

Performance


Trace, «Memory» Performance, JS .



JS V8 , . . , GC 30 1200 JS, 1/40.

Memory


.



.



, . , , , V8 , . , .

, , Q ( compiled code) — React . , ?

, , , .

, .



, , , . , — 4 . , .



React, - : . , JSX.

Performance Memory , :

  • Chromium: about:tracing.
  • Firefox: about:memory about:performance, .
  • Node — trace-gc, —expose-gc, require('trace_events'). trace_events .

परिणाम


  • , , , .
  • .
  • . , ?
  • , - .
  • SPA, , 1 , .
  • , - .

: flapenguin.me , Twitter , GitHub .

- ++ . YouTube-
.

, 2018 , . Frontend Conf 2018.

, :)

Source: https://habr.com/ru/post/hi433318/


All Articles