2009-03-28 15 views
5

मैं वर्तमान में एक सी आधारित अनुप्रयोग पर काम कर रहा हूं, एक गैर-एंटीपेटर्न फैशन में स्मृति मुक्त करने पर थोड़ा फंस गया हूं। मैं एक स्मृति प्रबंधन शौकिया हूँ।सी में स्मृति मुक्त करने के लिए पैटर्न?

मेरी मुख्य समस्या यह है कि मैं विभिन्न अलग-अलग क्षेत्रों में मेमोरी संरचनाएं घोषित करता हूं, और ये संरचनाएं अन्य कार्यों के संदर्भ में पारित हो जाती हैं। उनमें से कुछ कार्य त्रुटियों को फेंक सकते हैं और बाहर निकलें()।

यदि मैं एक दायरे में बाहर निकलता हूं तो मैं अपने संरचनाओं को मुक्त करने के बारे में कैसे जा सकता हूं, लेकिन मेरे सभी डेटा संरचनाएं उस दायरे में नहीं हैं?

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

उत्तर

3

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

अद्यतन: आपके आवेदन में थ्रेडिंग यह बहुत जटिल बना देगा। थ्रेडिंग मुद्दों के बारे में अन्य उत्तरों देखें।

+0

आप एटएक्सिट() फ़ंक्शन का भी उपयोग कर सकते हैं और लिंक किए गए सूची (जो इस मामले में वैश्विक चर होना चाहिए) पर आवंटित सभी मेमोरी को मुक्त करने के लिए एक फ़ंक्शन लिख सकते हैं - बाहर निकलने के लिए केवल एक सादा कॉल पर) - जिस तरह से आप सादा निकास() का उपयोग न करने के लिए याद रखना होगा। –

+0

लेकिन क्यों? ऑपरेटिंग सिस्टम पहले ही आवंटित स्मृति की एक सूची बनाए रख रहा है। आप उस कार्यक्षमता को डुप्लिकेट कर रहे हैं, जो कि अहंकार मुद्रास्फीति के अलावा कुछ लाभ के बिना शट-डाउन प्रक्रिया को धीमा कर रहा है। जब तक आप एमएसडीओएस को अपना आवेदन पोर्ट करने की योजना नहीं बनाते, तो ओएस को अपना काम करने दें। – Eclipse

+0

ऐसा क्यों है क्योंकि अंततः अनुशासन और funcitonality आपको स्मृति रिसाव को ट्रैक करने में मदद करेगा। यदि आप इसे सही तरीके से कोड करते हैं तो इसे डेफिन या मैक्रो परिभाषाओं के माध्यम से बंद कर दिया जा सकता है। – ojblass

3

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

+0

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

+0

यह केवल अच्छे ऑपरेटिंग सिस्टम के लिए है। जब आप आदत में पहुंचने के लिए इसे पूरा करते हैं, तो यह मुफ्त() स्मृति के लिए अच्छा है। –

+0

ठीक है, मुझे लगता है कि वह महत्वपूर्ण असफलताओं पर चर्चा कर रहा है; यानी, प्रक्रिया मर रही है और तेजी से मर रहा है। – Michael

1

आप मॉलोकैड मेमोरी के लिए एक सरल मेमोरी मैनेजर बना सकते हैं जो स्कॉप्स/फ़ंक्शंस के बीच साझा किया जाता है।

इसे मॉलोक करते समय इसे पंजीकृत करें, इसे मुक्त करते समय इसे पंजीकृत करें। एक ऐसा फ़ंक्शन है जो बाहर निकलने से पहले सभी पंजीकृत मेमोरी को मुक्त करता है।

यह थोड़ा ओवरहेड जोड़ता है, लेकिन यह स्मृति का ट्रैक रखने में मदद करता है। यह आपको पस्की मेमोरी लीक का शिकार करने में भी मदद कर सकता है।

3

मुझे लगता है कि इस सवाल का उचित जवाब देना है, हमें आपके पूरे कार्यक्रम (या सिस्टम, या जो भी मामला हो) के आर्किटेक्चर के बारे में जानना होगा।

उत्तर है: यह निर्भर करता है। ऐसी कई रणनीतियां हैं जिनका आप उपयोग कर सकते हैं।

जैसा कि अन्य ने आधुनिक डेस्कटॉप या सर्वर ऑपरेटिंग सिस्टम पर बताया है, आप exit() कर सकते हैं और आपके प्रोग्राम को आवंटित स्मृति के बारे में चिंता न करें।

यह रणनीति बदलती है, उदाहरण के लिए, यदि आप एक एम्बेडेड ऑपरेटिंग सिस्टम पर विकास कर रहे हैं जहां exit() सबकुछ साफ नहीं कर सकता है। आम तौर पर जो मैं देखता हूं वह है कि जब व्यक्तिगत फ़ंक्शन किसी त्रुटि के कारण वापस आते हैं, तो वे स्वयं को आवंटित किए गए किसी भी चीज़ को साफ़ करना सुनिश्चित करते हैं। कॉल करने के बाद, आप 10 कार्यों को कॉल करने के बाद exit() कॉल नहीं देखेंगे। प्रत्येक फ़ंक्शन बदले में एक त्रुटि इंगित करेगा जब यह लौटाता है, और प्रत्येक फ़ंक्शन स्वयं के बाद साफ हो जाएगा। मूल main() फ़ंक्शन (यदि आप करेंगे - इसे main() नहीं कहा जा सकता है) त्रुटि का पता लगाएगा, आवंटित की गई किसी भी मेमोरी को साफ करेगा, और उचित कार्यवाही करेगा।

जब आपके पास स्कॉप्स-इन-स्कोप्स हैं, तो यह रॉकेट विज्ञान नहीं है।जहां यह मुश्किल हो जाता है यदि आपके पास निष्पादन के कई धागे हैं, और साझा डेटा संरचनाएं हैं। फिर आपको कचरा कलेक्टर या संदर्भों की गणना करने का एक तरीका हो सकता है और जब स्मृति के अंतिम उपयोगकर्ता इसके साथ किया जाता है तो स्मृति को मुक्त कर सकते हैं। उदाहरण के लिए, यदि आप बीएसडी नेटवर्किंग स्टैक के स्रोत को देखते हैं, तो आप देखेंगे कि यह कुछ संरचनाओं में refcnt (संदर्भ गणना) मान का उपयोग करता है जिसे एक विस्तृत अवधि के लिए "जीवित" रखने की आवश्यकता होती है और अलग-अलग साझा की जाती है उपयोगकर्ताओं। (यह मूल रूप से कचरा कलेक्टर भी करता है।)

0

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

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

1

माइकल की सलाह अच्छी है - अगर आप बाहर निकल रहे हैं, तो आपको स्मृति को मुक्त करने की चिंता करने की आवश्यकता नहीं है क्योंकि सिस्टम इसे फिर से प्राप्त करेगा।

उसमें एक अपवाद साझा स्मृति खंड है - कम से कम सिस्टम वी साझा मेमोरी के तहत। वे सेगमेंट प्रोग्राम बनाने से लंबे समय तक बना सकते हैं जो उन्हें बनाता है।

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

एक सामान्य स्मृति अनुशासन के रूप में, हर बार जब आप गतिशील रूप से स्मृति आवंटित करते हैं, तो आपको समझना चाहिए कि कौन सा कोड इसे जारी करने जा रहा है और इसे कब जारी किया जा सकता है। कुछ मानक पैटर्न हैं। सबसे आसान "इस फ़ंक्शन में आवंटित किया गया है; इस फ़ंक्शन रिटर्न से पहले जारी किया गया"। यह स्मृति को बड़े पैमाने पर नियंत्रण में रखता है (यदि आप उस लूप पर बहुत अधिक पुनरावृत्तियों को नहीं चलाते हैं जिसमें स्मृति आवंटन होता है), और इसे स्कॉप्स करता है ताकि इसे वर्तमान फ़ंक्शन और उसके द्वारा किए जाने वाले कार्यों के लिए उपलब्ध कराया जा सके। जाहिर है, आपको उचित रूप से यह सुनिश्चित करना होगा कि आपके द्वारा कॉल किए जाने वाले कार्यों को डेटा पर गिलहरी (कैश) पॉइंटर्स नहीं जा रहे हैं और आपके द्वारा रिलीज़ होने और स्मृति को पुन: उपयोग करने के बाद बाद में उनका पुन: उपयोग करने का प्रयास किया जा रहा है।

अगले मानक पैटर्न को fopen() और fclose() द्वारा उदाहरण दिया गया है; एक ऐसा फ़ंक्शन है जो कुछ मेमोरी के लिए पॉइंटर आवंटित करता है, जिसे कॉलिंग कोड द्वारा उपयोग किया जा सकता है, और उसके बाद प्रोग्राम समाप्त होने पर रिलीज़ हो जाता है। हालांकि, यह अक्सर पहले मामले के समान ही बन जाता है - आमतौर पर नामक फ़ंक्शन में fclose() पर कॉल करना एक अच्छा विचार है।

शेष शेष 'पैटर्न' कुछ हद तक विज्ञापन हैं।

0

यह बोहेम कचरा कलेक्टर के लिए एक कार्य की तरह लगता है।

http://www.hpl.hp.com/personal/Hans_Boehm/gc/

पाठ्यक्रम है कि क्या आप या इसका इस्तेमाल करने के लिए खर्च कर सकते हैं चाहिए की प्रणाली पर निर्भर करता है।

1

लोगों ने पहले ही बताया है कि यदि आप त्रुटि के मामले में अपने कोड से बाहर निकल रहे हैं (या निरस्त कर रहे हैं) तो शायद आपको स्मृति मुक्त करने की चिंता करने की आवश्यकता नहीं है। लेकिन सिर्फ मामले में, यहां एक पैटर्न है जिसे मैंने विकसित किया है और त्रुटि के मामले में संसाधन बनाने और निकालने के लिए बहुत कुछ उपयोग किया है।नोट: मैं एक बिंदु बनाने के लिए यहां एक पैटर्न दिखा रहा हूं, असली कोड नहीं लिख रहा हूं!

int foo_create(foo_t *foo_out) { 
    int res; 
    foo_t foo; 
    bar_t bar; 
    baz_t baz; 
    res = bar_create(&bar); 
    if (res != 0) 
     goto fail_bar; 
    res = baz_create(&baz); 
    if (res != 0) 
     goto fail_baz; 
    foo = malloc(sizeof(foo_s)); 
    if (foo == NULL) 
     goto fail_alloc; 
    foo->bar = bar; 
    foo->baz = baz; 
    etc. etc. you get the idea 
    *foo_out = foo; 
    return 0; /* meaning OK */ 

    /* tear down stuff */ 
fail_alloc: 
    baz_destroy(baz); 
fail_baz: 
    bar_destroy(bar); 
fail_bar: 
    return res; /* propagate error code */ 
} 

मैं शर्त लगा सकता मैं कुछ टिप्पणी करते हुए कहा, "यह बुरा है, क्योंकि आप गोटो का उपयोग करें" प्राप्त करने के लिए जा रहा हूँ। लेकिन यह गोटो का एक अनुशासित और संरचित उपयोग है जो कोड को स्पष्ट, सरल और आसान बनाए रखने के लिए आसान बनाता है। आप इसके बिना कोड के माध्यम से एक सरल, प्रलेखित आंसू-नीचे पथ प्राप्त नहीं कर सकते हैं।

यदि आप इसे वास्तविक उपयोग में वाणिज्यिक कोड में देखना चाहते हैं, तो arena.c from the MPS (जो संयोग से स्मृति प्रबंधन प्रणाली है) पर एक नज़र डालें।

यह एक प्रकार का गरीब आदमी का प्रयास है ... हैंडलर खत्म करें, और आपको विनाशकों की तरह कुछ देता है।

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

आपके प्रश्न के बारे में कहने के लिए कई अन्य चीजें हैं - मैं इसे उपयोगी होने पर इस पैटर्न के साथ छोड़ने जा रहा हूं।

+0

यह खराब है क्योंकि आप गोटो का उपयोग करते हैं। क्षमा करें, विरोध नहीं कर सका: डी –

+0

यदि आपने 'नल' के साथ अपना 'बाज' और 'बार' शुरू किया है और अपना विनाश कार्य किया है, तो वे 'NULL' पैरामीटर को अच्छी तरह से संभालते हैं (जैसे' फ्री'), तो आप चाहते हैं केवल एक 'असफल' का उपयोग करने में सक्षम हो: 'लेबल, इसे थोड़ा कम भीड़ बना रहा है। –

+0

सुझाव के लिए धन्यवाद! यह बहुत अधिक कुछ भी के लिए न्यूल का उपयोग करने से बचने के लिए मेरी उच्च विश्वसनीयता परियोजनाओं में एक कोडिंग नियम है। नल के आसपास सभी प्रकार की त्रुटियों की ओर जाता है। हम विशेष रूप से एक विशेष कार्रवाई को इंगित करने के लिए NULL का उपयोग करने से बचें। लेकिन एक संपूर्ण नोटर निबंध है जो मैं नल के बारे में लिख सकता हूं: पी – rptb1

संबंधित मुद्दे