मेमोरी लीक जेम उपयोगकर्ताओं के लिए एक बड़ी समस्या है। इन्हें ट्रैक करना मुश्किल होता है और ये महंगे इन्फ्रास्ट्रक्चर लागत का कारण बन सकते हैं।
C एक्सटेंशन के भीतर मेमोरी लीक और भी बदतर होती है। रूबी में लीक खोजने के लिए कई टूल्स और लेख हैं, लेकिन C में आपको उन्हीं इंटर्नल्स तक पहुंच नहीं मिलती।
rb_funcall का भोला उपयोग मेमोरी लीक का कारण बन सकता है: इसकी बजाय rb_protect का उपयोग करना बेहतर है। इसलिए, यदि आप C एक्सटेंशन लेखक हैं, तो कृपया उन डेवलपर्स के लिए पढ़ें जो आपके जेम का उपयोग करेंगे।
शुरू करते हैं!
rb_funcall और C के साथ समस्या
rb_funcall एक बेहतरीन टूल हो सकता है जब आपको रूबी और अपने लाइब्रेरी के C भागों के बीच इंटरैक्ट करना हो लेकिन केवल थोड़ा C लिखना हो।
हालांकि, जब आप rb_funcall चलाते हैं, तो आप अब C में नहीं होते जहां सब कुछ सीधा होता है। यदि कॉल किया गया फंक्शन:
- रनटाइम के दौरान अपनी परिभाषा पूरी तरह से बदल देता है
- एक एक्सेप्शन उठाता है
तो आप मुश्किल में पड़ सकते हैं। नंबर 1 को पकड़ना सबसे आसान है। आपको संभवतः सेगफॉल्ट मिलेगा, और यदि आपका टेस्ट सूट पर्याप्त रूप से पूर्ण है, तो आपको प्रकाशन से पहले इसे पकड़ लेना चाहिए।
हालांकि, दूसरा कारण मेमोरी लीक का कारण बन सकता है और आपके कोडबेस को पढ़ना बहुत कठिन बना सकता है। आइए इसे अभी देखें।
रूबी में Raise के कारण C मेमोरी लीक
रूबी का रेजिंग मैकेनिज्म कोड के एक हिस्से से दूसरे हिस्से में तब तक कूदता है जब तक कि पहला पैरेंट नहीं मिल जाता जो एरर को पकड़ता है। यह MRI में longjmp और setjmp का उपयोग करके लागू किया गया है।
यदि आप इसमें रुचि रखते हैं कि यह कैसे बना है, तो रूबी हैकिंग गाइड का Evaluator अध्याय पढ़ें। संक्षेप में, जब आप begin..ensure ब्लॉक का उपयोग करते हैं, तो आप setjmp() करते हैं, और जब आप इस ब्लॉक के भीतर रेज करते हैं, तो आप सेव की गई पोजीशन पर longjmp() करते हैं।
इसलिए यदि rb_funcall के साथ कोई फंक्शन रेज होता है, तो उसके बाद का C कोड कभी निष्पादित नहीं होता।
नीचे दिया गया उदाहरण एक संभावित लीक को दर्शाता है। यदि json_parse रेज करता है, तो यह लीक करेगा।
बेशक, उपरोक्त उदाहरण थोड़ा मूर्खतापूर्ण है — आप फ्रीइंग और रूबी प्रोसेसिंग भागों को उल्टा कर सकते हैं। हालांकि, यह हमेशा संभव नहीं होता, और लंबे फंक्शन बॉडी अधिक जटिल हो सकते हैं।
रूबी में begin..ensure का उपयोग
यदि आप रूबी का उपयोग कर रहे हैं, तो आप उपरोक्त उदाहरण को begin..ensure का उपयोग करके लिख सकते हैं:
यह API C में rb_rescue और rb_ensure के साथ भी उपलब्ध है:
हालांकि, यह थोड़ा बोझिल है, और यदि आप एक rescue ब्लॉक जोड़ना चाहते हैं, तो यह पढ़ने में बहुत कम आसान हो जाता है। यदि आप C में begin..rescue..ensure..end API का उपयोग करना चाहते हैं, तो पीटर झू का 'A Rubyist's Walk Along the C-side (Part 8): Exceptions & Error Handling' पढ़ने का सुझाव देता हूं।
C के लिए rb_protect का उपयोग
एक और विकल्प है। पहले, देखते हैं कि यह रूबी में कैसा दिख सकता है:
यह रूबी में अजीब लगता है, लेकिन C के लिए बहुत उपयुक्त वर्कफ्लो है। MRI के पास इसके लिए एक API है, rb_protect, और C फंक्शन इस तरह दिखता है:
उपरोक्त मेथड सब कुछ फ्री करने के बाद रूबी एरर को फिर से उठाएगा।
ध्यान दें कि हम खाली rescue ब्लॉक का उपयोग करके एरर को नजरअंदाज करना भी चुन सकते हैं:
चेतावनी: यदि आप एरर को नहीं उठाते हैं, तो rb_set_errinfo(Qnil) चरण महत्वपूर्ण है ताकि आप उस एरर के बारे में जानकारी न रखें जिसे उपयोगकर्ताओं को नहीं जानना चाहिए।
या, आप सशर्त रूप से एरर उठाना चुन सकते हैं, जैसे rescue My::Error:
आप वास्तव में rb_errinfo() को $! ग्लोबल वेरिएबल के समान मान सकते हैं।
यह सब बहुत अच्छा है, लेकिन जब यह केवल एक rb_funcall तक सीमित हो जाता है, तो हम उस API को सरल बना सकते हैं।
rb_protect API का उपयोग करने का समग्र विचार जब कोई फंक्शन रेज कर सकता है, पठनीयता बढ़ाना है। आपको यह जांचने की आवश्यकता नहीं है कि फंक्शन रेज कर सकता है या नहीं, आप मान लेते हैं कि यह कर सकता है, और स्टेट का उपयोग करके उसके साथ काम करते हैं।
rb_protect_funcall प्रस्ताव
आइए rb_funcall को अलग करें, क्योंकि यह उपयोग करने वाला एकमात्र खतरनाक मेथड है। यहां एक API है जो ऐसा करेगा:
यह API rb_funcall के समान है, जिसमें rb_protect से एक state होता है। इसलिए उपयोग बहुत सीधा है:
यह API अभी तक रूबी में उपलब्ध नहीं है, और शायद कभी न हो। आप इसे RGeo (MIT LICENSE) से ले सकते हैं।
एक वास्तविक दुनिया का उदाहरण
यदि आप एक वास्तविक दुनिया का उदाहरण देखना चाहते हैं, तो मैं आपको RGeo कोडबेस पढ़ने के लिए प्रोत्साहित करता हूं क्योंकि हमने हाल ही में पूर्ण rb_protect पर स्विच किया है। हमारे पास rgeo_convert_to_geos_geometry जैसे कुछ फंक्शन भी हैं, जो सरल उपयोग के लिए इस स्टेट को प्रोपेगेट करते हैं। यह फंक्शन खोदना शुरू करने के लिए एक अच्छी जगह है।
RGeo पर एक इश्यू खोलने के लिए स्वतंत्र महसूस करें ताकि हमने जो विकल्प चुने हैं, उन पर आगे चर्चा कर सकें।
समापन
इस पोस्ट में, हमने C के साथ rb_funcall का उपयोग करने के खिलाफ चेतावनी दी क्योंकि यह मेमोरी लीक का कारण बन सकता है। हमने begin..ensure या rb_protect का उपयोग करने का पता लगाया।
हैप्पी कोडिंग!
P.S. यदि आप रूबी मैजिक पोस्ट को प्रेस से बाहर आते ही पढ़ना चाहते हैं, तो हमारे रूबी मैजिक न्यूज़लेटर की सदस्यता लें और कभी भी एक भी पोस्ट मिस न करें!

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