C ++ में स्टैक, स्टैटिक और हीप


160

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

मैंने सुना है कि अन्य भाषाएं "कचरा संग्रहकर्ता" को शामिल करती हैं, इसलिए आपको स्मृति के बारे में चिंता करने की आवश्यकता नहीं है। कचरा उठाने वाला क्या करता है?

आप अपने आप से मेमोरी में हेरफेर कर सकते हैं जो आप इस कचरा कलेक्टर का उपयोग नहीं कर सकते हैं?

एक बार किसी ने मुझसे कहा कि इस घोषणा के साथ:

int * asafe=new int;

मेरे पास "पॉइंटर टू ए पॉइंटर" है। इसका क्या मतलब है? यह अलग है:

asafe=new int;

?


कुछ समय पहले एक ऐसा ही सवाल पूछा गया था: ढेर और ढेर क्या हैं और कहाँ हैं? उस प्रश्न के कुछ बहुत अच्छे उत्तर हैं, जो आपके ऊपर कुछ प्रकाश डालते हैं।
स्कॉट साद

जवाबों:


223

एक ऐसा ही सवाल पूछा गया था, लेकिन इसने स्टैटिक्स के बारे में नहीं पूछा।

स्टैटिक, हीप और स्टैक मेमोरी क्या है, इसका सारांश:

  • एक स्थिर चर मूल रूप से एक वैश्विक चर है, भले ही आप इसे विश्व स्तर पर एक्सेस न कर सकें। आमतौर पर इसके लिए एक पता है जो निष्पादन योग्य में है। पूरे कार्यक्रम के लिए केवल एक प्रति है। कोई फर्क नहीं पड़ता कि आप कितनी बार एक फंक्शन कॉल (या क्लास) में जाते हैं (और कितने थ्रेड में!) वेरिएबल एक ही मेमोरी लोकेशन का जिक्र कर रहा है।

  • ढेर स्मृति का एक गुच्छा है जिसका उपयोग गतिशील रूप से किया जा सकता है। यदि आप किसी ऑब्जेक्ट के लिए 4kb चाहते हैं तो डायनेमिक एलोकेटर हीप में खाली जगह की सूची के माध्यम से देखेगा, एक 4kb चंक को बाहर निकालेगा, और आपको देगा। आम तौर पर, डायनेमिक मेमोरी एलोकेटर (मॉलोक, न्यू, एट सी।) मेमोरी के अंत में शुरू होता है और पीछे की ओर काम करता है।

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

जब आप प्रत्येक का उपयोग करना चाहेंगे:

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

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

  • जब आप ऊपर से अधिक लचीले होना चाहते हैं तो ढेर आवंटन (गतिशील रूप से आवंटित मेमोरी) उपयोगी है। अक्सर, एक फ़ंक्शन को किसी घटना पर प्रतिक्रिया देने के लिए बुलाया जाता है (उपयोगकर्ता "बॉक्स बनाएं" बटन पर क्लिक करता है)। उचित प्रतिक्रिया को एक नई वस्तु (एक नया बॉक्स ऑब्जेक्ट) आवंटित करने की आवश्यकता हो सकती है जो फ़ंक्शन के बाहर निकलने के बाद लंबे समय तक रहना चाहिए, इसलिए यह स्टैक पर नहीं हो सकता है। लेकिन आप यह नहीं जानते हैं कि आप प्रोग्राम की शुरुआत में कितने बॉक्स चाहते हैं, इसलिए यह स्थिर नहीं हो सकता है।

कचरा इकठा करना

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

कचरा संग्रह एक शानदार तंत्र है जब प्रदर्शन कोई बहुत बड़ा मुद्दा नहीं है। मुझे लगता है कि GCs बेहतर और अधिक परिष्कृत हो रहे हैं, लेकिन तथ्य यह है, आपको एक प्रदर्शन जुर्माना (उपयोग के मामले के आधार पर) को स्वीकार करने के लिए मजबूर किया जा सकता है। और अगर आप आलसी हैं, तो भी यह ठीक से काम नहीं कर सकता है। सबसे अच्छे समय में, गारबेज कलेक्टर्स को एहसास होता है कि आपकी याददाश्त चली जाती है जब यह पता चलता है कि इसके लिए कोई संदर्भ नहीं हैं ( संदर्भ गिनती देखें))। लेकिन, यदि आपके पास एक ऐसी वस्तु है जो स्वयं को संदर्भित करती है (संभवतः किसी अन्य वस्तु को संदर्भित करके जो वापस संदर्भित होती है), तो अकेले संदर्भ की गणना यह संकेत नहीं देगी कि स्मृति को हटाया जा सकता है। इस मामले में, जीसी को पूरे संदर्भ सूप को देखने और यह पता लगाने की आवश्यकता है कि क्या कोई द्वीप हैं जो केवल स्वयं द्वारा संदर्भित हैं। ऑफहैंड, मुझे लगता है कि ओ (एन ^ 2) ऑपरेशन होना चाहिए, लेकिन जो कुछ भी है, वह खराब हो सकता है यदि आप प्रदर्शन से संबंधित हैं। (संपादित करें: मार्टिन बी बताते हैं कि यह उचित रूप से कुशल एल्गोरिदम के लिए ओ (एन) है। यह अभी भी ओ (एन) बहुत अधिक है अगर आप प्रदर्शन से चिंतित हैं और कचरा संग्रह के बिना निरंतर समय में निपटा सकते हैं।)

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


यह वास्तव में मूल प्रश्न (या वास्तव में बहुत कुछ) के लिए प्रासंगिक नहीं है, लेकिन आपको स्टैक और हीप के स्थान पीछे की ओर मिले हैं। आमतौर पर , स्टैक नीचे बढ़ता है और ढेर बढ़ता है (हालांकि एक ढेर वास्तव में "बढ़ता" नहीं है, इसलिए यह एक बड़ा ओवरसिलेशन है) ...
पी डैडी

मुझे नहीं लगता कि यह प्रश्न अन्य प्रश्न के समान है या डुप्लिकेट भी है। यह विशेष रूप से C ++ के बारे में है और उसका मतलब है कि C ++ में मौजूद तीन स्टोरेज ड्यूरेशन लगभग निश्चित रूप से हैं। आपके पास स्थिर स्मृति पर आवंटित एक गतिशील वस्तु हो सकती है, ठीक है, उदाहरण के लिए, नया ओवरलोड।
जोहान्स शहाब -

7
कचरा संग्रहण का आपका अपमानजनक उपचार मददगार से थोड़ा कम था।
पी डैडी

9
अक्सर कचरा संग्रह आजकल मैनुअल फ्रीजिंग मेमोरी की तुलना में बेहतर है क्योंकि ऐसा तब होता है जब बहुत कम काम करना होता है, जैसा कि मेमोरी को मुक्त करने का विरोध करना होता है जब प्रदर्शन अन्यथा उपयोग किया जा सकता है।
जॉर्ज स्कोली

3
बस एक छोटी सी टिप्पणी - कचरा संग्रह में O (n ^ 2) जटिलता नहीं है (जो वास्तव में, प्रदर्शन के लिए विनाशकारी होगा)। एक कचरा संग्रहण चक्र के लिए लिया गया समय हीप के आकार के समानुपाती होता है - hpl.hp.com/personal/Hans_Boehm/gc/complexity.html देखें ।
मार्टिन बी

54

निम्नलिखित बिल्कुल बिल्कुल सटीक नहीं है। जब आप इसे पढ़ते हैं तो इसे नमक के दाने के साथ लें :)

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


स्वचालित भंडारण अवधि

आप अल्पकालिक और छोटे डेटा के लिए स्वचालित भंडारण अवधि का उपयोग करते हैं , जो केवल कुछ ब्लॉक के भीतर स्थानीय स्तर पर आवश्यक है :

if(some condition) {
    int a[3]; // array a has automatic storage duration
    fill_it(a);
    print_it(a);
}

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


स्थिर भंडारण अवधि

आप मुफ्त चर के लिए स्थिर भंडारण अवधि का उपयोग करते हैं, जो किसी भी कोड द्वारा हर समय पहुँचा जा सकता है, यदि उनका दायरा ऐसे उपयोग (नेमस्पेस स्कोप) की अनुमति देता है, और स्थानीय चर के लिए जिन्हें अपने दायरे (स्थानीय दायरे) से बाहर निकलने के लिए अपने जीवनकाल का विस्तार करने की आवश्यकता होती है, और सदस्य चरों के लिए जिन्हें उनके वर्ग (वर्ग दायरे) की सभी वस्तुओं द्वारा साझा किए जाने की आवश्यकता है। उनका जीवनकाल उस दायरे पर निर्भर करता है जिसमें वे हैं। उनके पास नाम स्थान और स्थानीय गुंजाइश और वर्ग गुंजाइश हो सकती है । उन दोनों के बारे में क्या सच है, एक बार उनका जीवन शुरू हो जाता है, जीवनकाल कार्यक्रम के अंत में समाप्त होता है । यहाँ दो उदाहरण हैं:

// static storage duration. in global namespace scope
string globalA; 
int main() {
    foo();
    foo();
}

void foo() {
    // static storage duration. in local scope
    static string localA;
    localA += "ab"
    cout << localA;
}

प्रोग्राम प्रिंट करता है ababab, क्योंकि localAइसके ब्लॉक से बाहर निकलने पर नष्ट नहीं होता है। आप कह सकते हैं कि जिन वस्तुओं पर स्थानीय गुंजाइश होती है, वे जीवनकाल शुरू करते हैं जब नियंत्रण उनकी परिभाषा तक पहुंचता है । इसके लिए localA, यह तब होता है जब फ़ंक्शन के शरीर में प्रवेश किया जाता है। नेमस्पेस स्कोप में वस्तुओं के लिए, प्रोग्राम स्टार्टअप पर जीवनकाल शुरू होता है । वर्ग गुंजाइश की स्थिर वस्तुओं के लिए भी यही सच है:

class A {
    static string classScopeA;
};

string A::classScopeA;

A a, b; &a.classScopeA == &b.classScopeA == &A::classScopeA;

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


गतिशील भंडारण अवधि

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

int main() {
    // the object that s points to has dynamic storage 
    // duration
    string *s = new string;
    // pass a pointer pointing to the object around. 
    // the object itself isn't touched
    foo(s);
    delete s;
}

void foo(string *s) {
    cout << s->size();
}

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

int main() {
    shared_ptr<string> s(new string);
    foo(s);
}

void foo(shared_ptr<string> s) {
    cout << s->size();
}

आपको कॉल डिलीट करने की परवाह नहीं है: साझा किया गया ptr आपके लिए ऐसा करता है, अगर ऑब्जेक्ट को रेफर करने वाला आखिरी पॉइंटर गुंजाइश से बाहर हो जाता है। साझा ptr में ही स्‍वचालित संग्रहण अवधि होती है। इसलिए इसका जीवनकाल स्वचालित रूप से प्रबंधित हो जाता है, जिससे यह जांचने की अनुमति मिलती है कि क्या इसे अपने विध्वंसक में इंगित गतिशील वस्तु को हटाना चाहिए या नहीं। Share_ptr संदर्भ के लिए, बढ़ावा देने वाले दस्तावेज़ देखें: http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/sared_ptr.htm


39

इसे विस्तृत रूप से कहा गया है, जैसा कि "संक्षिप्त उत्तर":

  • स्थिर चर (वर्ग)
    आजीवन = कार्यक्रम रनटाइम (1)
    दृश्यता = पहुंच संशोधक (निजी / संरक्षित / सार्वजनिक) द्वारा निर्धारित

  • स्थिर चर (वैश्विक गुंजाइश)
    आजीवन = कार्यक्रम रनटाइम (1)
    दृश्यता = संकलन इकाई जो इसमें (2) में तत्काल है

  • हीप वेरिएबल
    लाइफटाइम = आपके द्वारा परिभाषित (नए को हटाने के लिए)
    दृश्यता = आपके द्वारा परिभाषित (आप जो भी पॉइंटर असाइन करते हैं)

  • स्टैक वेरिएबल
    विजिबिलिटी = डिक्लेरेशन से तब तक जब तक कि स्कोप
    लाइफटाइम से बाहर न हो जाए = डिक्लेरेशन से तब तक जब तक स्कोप डिक्लेयर नहीं हो जाता


(1) अधिक सटीक: संकलन इकाई (यानी C / C ++ फ़ाइल) के विचलन से आरंभ होने तक। संकलन इकाइयों के आरंभ का क्रम मानक द्वारा परिभाषित नहीं है।

(२) खबरदार: यदि आप किसी हेडर में स्टेटिक वैरिएबल को इंस्टेंट करते हैं, तो प्रत्येक संकलन यूनिट को अपनी कॉपी मिलती है।


5

मुझे यकीन है कि जल्द ही एक बेहतर उत्तर के साथ एक पेडेंट आएगा, लेकिन मुख्य अंतर गति और आकार है।

ढेर

आवंटित करने के लिए नाटकीय रूप से तेज़। यह ओ (1) में किया जाता है क्योंकि इसे स्टैक फ्रेम स्थापित करते समय आवंटित किया जाता है ताकि यह अनिवार्य रूप से मुक्त हो। दोष यह है कि यदि आप स्टैक स्थान से बाहर निकलते हैं तो आप बंधे हुए हैं। आप स्टैक आकार को समायोजित कर सकते हैं, लेकिन IIRC आपके पास खेलने के लिए ~ 2MB है। इसके अलावा, जैसे ही आप फ़ंक्शन से बाहर निकलते हैं, स्टैक पर सब कुछ साफ़ हो जाता है। इसलिए बाद में इसे संदर्भित करने के लिए समस्याग्रस्त हो सकता है। (आवंटित वस्तुओं को ढेर करने के लिए संकेत बग की ओर जाता है।)

ढेर

आवंटित करने के लिए नाटकीय रूप से धीमा। लेकिन आपके पास खेलने के लिए जीबी है, और इंगित करने के लिए।

कचरा इकट्ठा करने वाला

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


3

स्थिर और स्टैक की समस्याएं क्या हैं?

"स्थैतिक" आवंटन के साथ समस्या यह है कि आवंटन संकलन-समय पर किया जाता है: आप इसका उपयोग कुछ चर संख्या डेटा को आवंटित करने के लिए नहीं कर सकते हैं, जिसकी संख्या रन-टाइम तक ज्ञात नहीं है।

"स्टैक" के आवंटन के साथ समस्या यह है कि आवंटन को नष्ट कर दिया जाता है जैसे ही सबरूटीन जो आवंटन रिटर्न करता है।

मैं ढेर में चर आवंटित किए बिना एक पूरा आवेदन लिख सकता था?

शायद, लेकिन एक गैर-तुच्छ, सामान्य, बड़ा अनुप्रयोग नहीं (लेकिन तथाकथित "एम्बेडेड" प्रोग्राम ढेर के बिना लिखा जा सकता है, सी ++ के सबसेट का उपयोग करके)।

कचरा उठाने वाला क्या करता है?

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

कचरा संग्रहकर्ता C ++ प्रोग्रामिंग की सामान्य विशेषता नहीं हैं।

आप अपने आप से मेमोरी में हेरफेर कर सकते हैं जो आप इस कचरा कलेक्टर का उपयोग नहीं कर सकते हैं?

नियतात्मक स्मृति व्यवहार के लिए C ++ तंत्र सीखें:

  • 'स्थैतिक': कभी नहीं किया गया
  • 'स्टैक': जैसे ही चर "दायरे से बाहर हो जाता है"
  • 'हीप': जब पॉइंटर को हटा दिया जाता है (स्पष्ट रूप से एप्लिकेशन द्वारा हटा दिया जाता है, या कुछ-या-अन्य सबरूटीन के भीतर अंतर्निहित रूप से हटा दिया जाता है)

1

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

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


1

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


0

कुछ स्थितियों में जीसी का एक फायदा दूसरों में झुंझलाहट है; जीसी पर निर्भरता इसके बारे में ज्यादा नहीं सोचने के लिए प्रोत्साहित करती है। सिद्धांत रूप में, 'निष्क्रिय' अवधि तक या इसके पूरी तरह से होने तक प्रतीक्षा करता है, जब यह बैंडविड्थ चुराएगा और आपके ऐप में प्रतिक्रिया विलंबता का कारण होगा।

लेकिन आपको इसके बारे में नहीं सोचना है। ' बस के रूप में multithreaded क्षुधा में सब कुछ के साथ, जब आप उपज कर सकते हैं, आप उपज कर सकते हैं। तो उदाहरण के लिए, .Net में, GC से अनुरोध करना संभव है; ऐसा करने से, कम लगातार चलने वाले GC के बजाय, आप अधिक लगातार चलने वाले GC को कम कर सकते हैं, और इस ओवरहेड से जुड़े विलंबता को फैला सकते हैं।

लेकिन यह GC के प्राथमिक आकर्षण को पराजित करता है जो प्रतीत होता है "इसके बारे में ज्यादा सोचने की जरूरत नहीं है क्योंकि यह ऑटो-मैट-आईसी है।"

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

मेरे पास कुछ समय पहले एक ग्रीष्मकालीन छात्र था, एक इंटर्न, स्मार्ट बच्चा, जो जीसी पर छूट गया था; वह GC के महात्वाकांक्षी के बारे में इतना दृढ़ था कि जब मानव रहित C / C ++ में प्रोग्रामिंग की गई, तब भी उसने मॉलॉक / मुफ्त नए / हटाए गए मॉडल का पालन करने से इनकार कर दिया, क्योंकि, "आपको आधुनिक प्रोग्रामिंग भाषा में ऐसा नहीं करना चाहिए।" और आप जानते हैं? छोटे, कम चलने वाले ऐप्स के लिए, आप वास्तव में उससे दूर हो सकते हैं, लेकिन लंबे समय तक चलने वाले ऐप के लिए नहीं।


0

स्टैक कंपाइलर द्वारा आवंटित एक मेमोरी है, जब हम कभी भी प्रोग्राम को कंपाइल करते हैं, डिफॉल्ट कंपाइलर में OS से कुछ मेमोरी आवंटित होती है (हम आपके IDE में कंपाइलर सेटिंग्स से सेटिंग बदल सकते हैं) और OS वह है जो आपको मेमोरी देता है, यह निर्भर करता है सिस्टम और कई अन्य चीजों पर उपलब्ध स्मृति पर, और स्टैक मेमोरी में आने पर आवंटित किया जाता है, जब हम एक चर घोषित करते हैं जिसे वे कॉपी करते हैं (फॉर्मल के रूप में) उन चर को स्टैक पर धकेल दिया जाता है जो वे विज़ुअल स्टूडियो में सीडीईसीएल को डिफ़ॉल्ट रूप से कुछ नामकरण परंपराओं का पालन करते हैं। ex: infix संकेतन: c = a + b; स्टैक पुशिंग को दाएं से बाएं PUSHING, b से स्टैक, ऑपरेटर, एक से स्टैक और परिणाम के लिए किया जाता है i, स्टैक के लिए। प्री फिक्स नोटेशन में: = + कैब यहां सभी वेरिएबल्स को 1 (दाएं से बाएं) स्टैक करने के लिए धकेला जाता है और फिर ऑपरेशन किया जाता है। संकलक द्वारा आवंटित यह मेमोरी तय हो गई है। तो मान लें कि 1MB मेमोरी हमारे एप्लिकेशन को आबंटित है, मान लीजिए कि चर का उपयोग 700kb मेमोरी (सभी स्थानीय चर स्टैक करने के लिए धकेल दिया जाता है जब तक कि उन्हें गतिशील रूप से आवंटित नहीं किया जाता है) इसलिए शेष 324kb मेमोरी को ढेर में आवंटित किया जाता है। और इस स्टैक का जीवनकाल कम होता है, जब फ़ंक्शन का दायरा समाप्त हो जाता है तो ये ढेर साफ हो जाते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.