हमने पाया है कि नीचे दिया गया पोस्ट नेट बर्कोपेक के 2017 के आर्टिकल 'Understanding Ruby GC through GC.stat' पर आधारित है। ऐसा प्रतीत होता है कि इस आर्टिकल के कुछ हिस्सों को प्लेजराइज़ किया गया था, जिसके बारे में हमें मूल लेखक द्वारा बताए जाने तक पता नहीं था। हम प्रकाशन से पहले अपने सभी आर्टिकल्स को प्लेजराइज़्म टूल के माध्यम से चलाते हैं, लेकिन इसने इसे नहीं पकड़ा। हम नेट और अपने पाठकों से इस अनजाने त्रुटि के लिए बड़ी माफी मांगते हैं।
यह महत्वपूर्ण है कि आप समझें कि Ruby में गार्बेज कलेक्शन कैसे काम करता है ताकि आप अपने ऐप के परफॉरमेंस पर पूर्ण नियंत्रण रख सकें।
इस पोस्ट में, हम देखेंगे कि Ruby में गार्बेज कलेक्शन को कैसे लागू और कस्टमाइज़ किया जाए।
चलिए शुरू करते हैं!
Ruby गार्बेज कलेक्टर मॉड्यूल
Ruby गार्बेज कलेक्टर मॉड्यूल Ruby के मार्क और स्वीप गार्बेज कलेक्शन मैकेनिज्म का एक इंटरफेस है।
जबकि यह आवश्यकता पड़ने पर बैकग्राउंड में स्वचालित रूप से चलता है, GC मॉड्यूल आपको जब भी आवश्यक हो GC को मैन्युअली कॉल करने और गार्बेज कलेक्शन साइकल्स कैसे चल रहे हैं, इसकी जानकारी प्राप्त करने देता है। मॉड्यूल कुछ पैरामीटर्स प्रदान करता है जिन्हें आप परफॉरमेंस को मॉडरेट करने के लिए बदल सकते हैं।
इस मॉड्यूल के कुछ सबसे अधिक उपयोग किए जाने वाले मेथड्स हैं:
- start/garbage_collect: यह मेथड मैन्युअली गार्बेज कलेक्शन साइकिल शुरू करता है।
- enable/disable: ये मेथड्स स्वचालित गार्बेज कलेक्शन साइकल्स को सक्षम या अक्षम करते हैं। वे एक बूलियन वैल्यू लौटाते हैं जो इंगित करता है कि ऑपरेशन सफल रहा या नहीं।
- stat: यह मेथड कुंजियों और मानों की एक सूची प्रदान करता है जो GC मॉड्यूल के परफॉरमेंस का वर्णन करते हैं। हम अगले सेक्शन में इन मेट्रिक्स को विस्तार से देखेंगे।
Ruby गार्बेज कलेक्टर पैरामीटर्स को समझना
यह समझने के लिए कि Ruby का GC आंतरिक रूप से कैसे काम करता है, आइए GC मॉड्यूल के मेट्रिक्स को देखें। ताज़ा बूट किए गए irb पर निम्नलिखित कमांड चलाएं:
puts GC.statआप देखेंगे कि आपकी स्क्रीन पर कई नंबर पॉप अप होते हैं, जो कुछ इस तरह दिखते हैं:
{ :count=>12, :heap_allocated_pages=>49, :heap_sorted_length=>49, :heap_allocatable_pages=>0, :heap_available_slots=>19975, :heap_live_slots=>19099, :heap_free_slots=>876, :heap_final_slots=>0, :heap_marked_slots=>16659, :heap_eden_pages=>49, :heap_tomb_pages=>0, :total_allocated_pages=>49, :total_freed_pages=>0, :total_allocated_objects=>66358, :total_freed_objects=>47259, :malloc_increase_bytes=>16216, :malloc_increase_bytes_limit=>16777216, :minor_gc_count=>10, :major_gc_count=>2, :remembered_wb_unprotected_objects=>191, :remembered_wb_unprotected_objects_limit=>312, :old_objects=>16024, :old_objects_limit=>23556, :oldmalloc_increase_bytes=>158824, :oldmalloc_increase_bytes_limit=>16777216 }इसमें रनटाइम में गार्बेज कलेक्शन कैसे हो रहा है, इसके बारे में सभी जानकारी होती है। आइए इनमें से प्रत्येक नंबर की विस्तार से जांच करें।
Ruby गार्बेज कलेक्टर में काउंट्स
हम इन कुंजियों का वर्णन करके शुरू करेंगे:
{ :count=>12, #… :minor_gc_count=>10, :major_gc_count=>2, }ये GC काउंट्स हैं, और वे काफी सीधी जानकारी देते हैं। minor_gc_count और major_gc_count प्रत्येक प्रकार के गार्बेज कलेक्शन रन की गिनती हैं।
Ruby में दो प्रकार के गार्बेज कलेक्शन होते हैं।
माइनर GC एक गार्बेज कलेक्शन प्रयास को संदर्भित करता है जो केवल उन ऑब्जेक्ट्स को गार्बेज कलेक्ट करने का प्रयास करता है जो नए हैं, यानी वे तीन या उससे कम गार्बेज कलेक्शन साइकल्स से बचे हैं।
दूसरी ओर, मेजर GC एक गार्बेज कलेक्शन प्रयास है जो सभी ऑब्जेक्ट्स को गार्बेज कलेक्ट करने का प्रयास करता है, यहां तक कि वे जो तीन से अधिक गार्बेज कलेक्शन साइकल्स से बचे हैं। count minor_gc_count और major_gc_count का योग है।
GC काउंट को ट्रैक करना कुछ कारणों से सहायक हो सकता है। आप यह पता लगा सकते हैं कि कोई विशेष जॉब या प्रोसेस हमेशा GC को ट्रिगर करता है और यह कितनी बार उन्हें ट्रिगर करता है। यह मल्टीथ्रेडेड एप्लिकेशन्स जैसे मामलों में 100% सटीक नहीं हो सकता है, लेकिन यह पता लगाने के लिए एक अच्छा प्रारंभिक बिंदु है कि आपकी मेमोरी कहां लीक हो रही है।
हीप नंबर्स: स्लॉट्स और पेजेस
आइए अब इन कुंजियों के बारे में बात करते हैं, जिन्हें हीप नंबर्स के रूप में भी जाना जाता है:
{ # पेज नंबर्स :heap_allocated_pages=>49, :heap_sorted_length=>49, :heap_allocatable_pages=>0, # स्लॉट्स :heap_available_slots=>19975, :heap_live_slots=>19099, :heap_free_slots=>876, :heap_final_slots=>0, :heap_marked_slots=>16659, # Eden और Tomb पेजेस :heap_eden_pages=>49, :heap_tomb_pages=>0, }यहां हम जिस हीप की बात कर रहे हैं वह एक C डेटा स्ट्रक्चर है। इसमें वर्तमान में लाइव सभी Ruby ऑब्जेक्ट्स के रेफरेंस होते हैं। एक हीप पेज मेमोरी स्लॉट्स से बना होता है, और प्रत्येक स्लॉट में केवल एक लाइव Ruby ऑब्जेक्ट की जानकारी होती है:
heap_allocated_pagesवर्तमान में आवंटित हीप पेजेस की संख्या है। ये पेजेस पूरी तरह से खाली, पूरी तरह से भरे, या आंशिक रूप से भरे हो सकते हैं।heap_sorted_lengthवह वास्तविक आकार है जो हीप ने मेमोरी में घेरा है और यहheap_allocated_pagesसे अलग है, क्योंकि लंबाई हीप पेजेस को एक साथ रखने की लंबाई है, न कि उनकी गिनती। यदि आपने शुरू में 10 पेजेस आवंटित किए और फिर सेट के बीच से एक को मुक्त कर दिया, तो आपकाheap_allocated_pages9 होगा, लेकिनheap_sorted_lengthअभी भी 10 होगा।- अंत में,
heap_allocatable_pagesउन हीप्स की गिनती है जो Ruby वर्तमान में रखता है जिन्हें जरूरत पड़ने पर उपयोग किया जा सकता है।
अब, स्लॉट्स की बात करते हैं:
heap_available_slotsहीप पेजेस में उपलब्ध स्लॉट्स की कुल संख्या है।heap_live_slotsमेमोरी में लाइव ऑब्जेक्ट्स की संख्या है।heap_free_slotsआवंटित हीप पेजेस में खाली स्लॉट्स हैं।heap_final_slotsउन स्लॉट्स की गिनती है जिनके ऑब्जेक्ट्स से फाइनलाइज़र्स जुड़े हुए हैं। फाइनलाइज़र्स Procs हैं जो ऑब्जेक्ट के मुक्त होने पर चलते हैं, OOPS में डिस्ट्रक्टर्स के समान।heap_marked_slotsपुराने ऑब्जेक्ट्स (यानी वे ऑब्जेक्ट्स जो 3 से अधिक GC साइकल्स से आसपास हैं) और राइट-बैरियर अनप्रोटेक्टेड ऑब्जेक्ट्स की गिनती है।
फिर हमारे पास tomb_pages और eden_pages हैं।
tomb_pages उन पेजेस की गिनती है जिनमें कोई लाइव ऑब्जेक्ट नहीं है। ये पेजेस अंततः Ruby द्वारा ऑपरेटिंग सिस्टम को वापस जारी कर दिए जाते हैं।
दूसरी ओर, eden_pages उन पेजेस की गिनती है जिनमें कम से कम एक लाइव ऑब्जेक्ट होता है, इसलिए उन्हें ऑपरेटिंग सिस्टम को वापस जारी नहीं किया जा सकता।
यदि आपके एप्लिकेशन में मेमोरी ब्लोट इश्यूज़ हैं तो मीट्रिक heap_free_slots की निगरानी करने पर विचार करें।
मुक्त स्लॉट्स की अधिक संख्या (250,000 से अधिक) आमतौर पर इंगित करती है कि आपके पास कुछ कंट्रोलर एक्शन्स हैं जो एक साथ कई ऑब्जेक्ट्स आवंटित करते हैं और फिर उन्हें मुक्त करते हैं। यह आपके रनिंग Ruby प्रोसेस के आकार को स्थायी रूप से बढ़ा सकता है।
क्युमुलेटिव नंबर्स
{ :total_allocated_pages=>49, :total_freed_pages=>0, :total_allocated_objects=>66358, :total_freed_objects=>47259, }ये नंबर प्रोसेस के पूरे जीवन के लिए क्युमुलेटिव या एडिटिव प्रकृति के हैं। वे GC द्वारा कभी रीसेट नहीं होते और तकनीकी रूप से नीचे नहीं जा सकते। ये चारों नंबर स्वयं व्याख्यात्मक हैं।
गार्बेज कलेक्शन थ्रेशोल्ड्स
इन नंबरों को समझने के लिए, आपको पहले यह समझने की आवश्यकता है कि GC कब ट्रिगर होता है:
{ :malloc_increase_bytes=>16216, :malloc_increase_bytes_limit=>16777216, :remembered_wb_unprotected_objects=>191, :remembered_wb_unprotected_objects_limit=>312, :old_objects=>16024, :old_objects_limit=>23556, :oldmalloc_increase_bytes=>158824, :oldmalloc_increase_bytes_limit=>16777216 }इस आम धारणा के विपरीत कि GC रन निश्चित अंतराल पर होते हैं, GC रन तब ट्रिगर होते हैं जब Ruby मेमोरी स्पेस से बाहर निकलने लगता है। माइनर GC तब होता है जब Ruby free_slots से बाहर निकल जाता है।
यदि माइनर GC रन के बाद भी Ruby free_slots पर कम है — या ओल्डमैलॉक, मैलॉक, ओल्ड ऑब्जेक्ट काउंट, या शेडी/राइट-बैरियर-अनप्रोटेक्टेड काउंट का थ्रेशोल्ड पार हो जाता है — तो एक मेजर GC रन ट्रिगर होता है। gc.stat का उपरोक्त भाग इन थ्रेशोल्ड्स के मूल्यों को दिखाता है।
malloc_increase_bytes उस मेमोरी की मात्रा को संदर्भित करता है जो अब तक चर्चा किए गए हीप के बाहर आवंटित की गई है। जब किसी ऑब्जेक्ट का आकार मेमोरी स्लॉट के मानक आकार — मान लीजिए 40 बाइट्स — से अधिक हो जाता है, तो Ruby उस ऑब्जेक्ट के लिए कहीं और कुछ स्पेस malloc करता है। जब कुल अतिरिक्त आवंटित स्पेस malloc_increase_bytes_limit से अधिक हो जाता है, तो एक मेजर GC ट्रिगर होता है।
oldmalloc_increase_bytes पुराने ऑब्जेक्ट्स के लिए एक समान थ्रेशोल्ड है। old_objects पुराने के रूप में चिह्नित ऑब्जेक्ट स्लॉट्स की गिनती है। जब संख्या old_objects_limit से अधिक हो जाती है, तो मेजर GC ट्रिगर होता है।
remembered_wb_unprotected_objects उन ऑब्जेक्ट्स की कुल गिनती है जो राइट-बैरियर द्वारा प्रोटेक्टेड नहीं हैं और रिमेम्बर्ड सेट का हिस्सा हैं।
राइट-बैरियर Ruby रनटाइम और उसके ऑब्जेक्ट्स के बीच एक इंटरफेस है, जो इंटरप्रेटर को ऑब्जेक्ट से और ऑब्जेक्ट के रेफरेंस को ट्रैक करने की अनुमति देता है जैसे ही वे बनाए जाते हैं।
C-एक्सटेंशन्स राइट-बैरियर का उपयोग किए बिना ऑब्जेक्ट्स के नए रेफरेंस बना सकते हैं, जिस स्थिति में ऑब्जेक्ट्स को शेडी या राइट-बैरियर अनप्रोटेक्टेड के रूप में चिह्नित किया जाता है। रिमेम्बर्ड सेट केवल पुराने ऑब्जेक्ट्स की एक सूची है जिनमें कम से कम एक नए ऑब्जेक्ट का रेफरेंस होता है।
Ruby गार्बेज कलेक्शन परफॉरमेंस को कस्टमाइज़ करें
अब जब आप समझते हैं कि Ruby GC आपके एप्लिकेशन की मेमोरी को कैसे प्रबंधित करता है, तो GC के व्यवहार को कस्टमाइज़ करने के लिए उपलब्ध विकल्पों को देखने का समय आ गया है।
यहां एनवायरनमेंट वेरिएबल्स हैं जिनका उपयोग आप Ruby GC के परफॉरमेंस को मॉडरेट करने और बदले में अपने एप्लिकेशन के परफॉरमेंस को बेहतर बनाने के लिए कर सकते हैं:
RUBY_GC_HEAP_INIT_SLOTS RUBY_GC_HEAP_FREE_SLOTS RUBY_GC_HEAP_GROWTH_FACTOR RUBY_GC_HEAP_GROWTH_MAX_SLOTS RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR और अन्य वेरिएबल्सआइए यहां महत्वपूर्ण पैरामीटर्स के बारे में एक-एक करके बात करते हैं:
RUBY_GC_HEAP_INIT_SLOTS: Ruby हीप पर स्लॉट्स की प्रारंभिक संख्या को परिभाषित करता है और डिफ़ॉल्ट रूप से 10000 पर सेट है। आप इस पैरामीटर को बदलना चाह सकते हैं यदि आप सुनिश्चित हैं कि आपका ऐप शुरू में अपने अधिकांश ऑब्जेक्ट्स आवंटित करेगा।RUBY_GC_HEAP_FREE_SLOTS: GC साइकिल के ठीक बाद उपलब्ध होने वाले मुक्त स्लॉट्स की न्यूनतम संख्या को नियंत्रित करता है। इसका डिफ़ॉल्ट मान 4096 है। यह मान रनटाइम के दौरान केवल एक बार पहले हीप ग्रोथ के दौरान उपयोग किया जाता है।RUBY_GC_HEAP_GROWTH_FACTOR: वह फैक्टर जिसके द्वारा Ruby इंटरप्रेटर के लिए उपलब्ध हीप्स बढ़ते हैं। इसका डिफ़ॉल्ट मान 1.8 है। इसे बदलने का कोई खास मतलब नहीं है, क्योंकि Ruby हीप ग्रोथ में पहले से ही आक्रामक है। यदि आप इसे घटाने की कोशिश करते हैं तो यह ज्यादा अंतर नहीं लाएगा क्योंकि आधुनिक इंटरप्रेटर्स में हीप्स ऑन-डिमांड आवंटित होते हैं।RUBY_GC_HEAP_GROWTH_MAX_SLOTS: एक बार में Ruby हीप स्पेस में जोड़ सकने वाले स्लॉट्स की अधिकतम संख्या। डिफ़ॉल्ट मान 0 है, जिसका अर्थ है संख्या पर कोई सीमा नहीं। यदि आपके ऐप को अपने जीवनकाल में लाखों ऑब्जेक्ट्स आवंटित करने की आवश्यकता है, तो आप इस पैरामीटर पर एक कैप लगाना चाह सकते हैं। हालांकि, इसका आपके ऐप के GC समय पर बहुत कम प्रभाव पड़ेगा।RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTORइंटरप्रेटर को मेजर GC साइकिल करने के लिए मजबूर करता है जब मेमोरी में कुल पुराने ऑब्जेक्ट्स की संख्या = इस संख्या x अंतिम GC साइकिल के बाद मेमोरी में पुराने ऑब्जेक्ट्स की संख्या से अधिक होती है। आप इस संख्या को बढ़ाना चाह सकते हैं यदि आपको लगता है कि आपके कई ऑब्जेक्ट्स पुरानी पीढ़ी में प्रवेश करने के बाद अनुपयोगी हो जाएंगे। हालांकि, इसकी शायद ही कभी आवश्यकता होती है।RUBY_GC_MALLOC_LIMITनई पीढ़ी के लिए मैलॉक कॉल्स की न्यूनतम सीमा है। इसका डिफ़ॉल्ट मान 16 MB है।RUBY_GC_MALLOC_LIMIT_MAXउसी मैलॉक कॉल्स के लिए अधिकतम सीमा है। इसका डिफ़ॉल्ट मान 32 MB है। यदि आपका एप्लिकेशन औसत से अधिक मेमोरी का उपयोग करता है तो आप इन दो सीमाओं को बढ़ाना चाह सकते हैं। हालांकि, इन्हें बहुत अधिक बढ़ाने से सावधान रहें। अन्यथा, वे उच्च पीक मेमोरी खपत का कारण बन सकते हैं। हमेशा इन सीमाओं को धीरे-धीरे बढ़ाएं — मान लीजिए 4 या 8 MB से।RUBY_GC_MALLOC_LIMIT_GROWTH_FACTORनई पीढ़ी के लिए मैलॉक सीमा का ग्रोथ फैक्टर है। इसका डिफ़ॉल्ट मान 1.4 है। यदि आपका ऐप एक बार में पूरी के बजाय टुकड़ों में मेमोरी आवंटित करता है तो आप इस संख्या को बढ़ाने पर विचार कर सकते हैं।- इसी तरह,
RUBY_GC_OLDMALLOC_LIMITऔरRUBY_GC_OLDMALLOC_LIMIT_MAXपुरानी पीढ़ी के लिए न्यूनतम और अधिकतम मैलॉक सीमाएं हैं। इन पैरामीटर्स के डिफ़ॉल्ट मान 16 MB और 128 MB हैं। RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTORइस सीमा का ग्रोथ फैक्टर है। इसका डिफ़ॉल्ट मान 1.2 है। आप अधिकतम प्रभाव के लिए इन्हें नई पीढ़ी की सीमाओं के साथ बदलने पर विचार कर सकते हैं।
Ruby में गार्बेज कलेक्शन को फाइन-ट्यून करना
हमने GC मॉड्यूल को कस्टमाइज़ करने के कुछ सामान्य और सरल तरीकों पर चर्चा की ताकि आप अपने एप्लिकेशन के समग्र परफॉरमेंस को बेहतर बनाने में मदद कर सकें। हालांकि, ये ट्वीक सभी मामलों में काम नहीं कर सकते हैं। आपको यह तय करने से पहले अपने ऐप के मेमोरी उपयोग पैटर्न का पता लगाना होगा कि क्या कस्टमाइज़ करना है।
दूसरी ओर, आप एक ऑटोमेटेड टेस्ट चलाने पर विचार कर सकते हैं जो आपके लिए इन पैरामीटर्स के सर्वोत्तम मूल्यों को ढूंढता है। TuneMyGC जैसे टूल आपके एनवायरनमेंट वेरिएबल्स के लिए सर्वोत्तम मूल्यों का सेट ढूंढने में काफी सीधे हैं।
निश्चित रूप से GC पैरामीटर्स को देखें यदि आपका एप्लिकेशन अजीब व्यवहार कर रहा है। यहां एक छोटा सा बदलाव आपके ऐप की मेमोरी खपत को कम करने और मेमोरी ब्लोट को रोकने में एक लंबा रास्ता तय कर सकता है।
मुझे आशा है कि इस आर्टिकल ने आपको इस बारे में एक अच्छा विचार दिया है कि अपने Ruby गार्बेज कलेक्शन मॉड्यूल को कस्टमाइज़ करते समय क्या देखना है। अधिक परिचय के लिए, गार्बेज कलेक्शन का परिचय भाग I और भाग II देखें।
हैप्पी कोडिंग!