क्या और कहाँ ढेर और ढेर हैं?


8099

प्रोग्रामिंग भाषा की किताबें समझाती हैं कि स्टैक पर मूल्य प्रकार बनाए जाते हैं , और संदर्भ प्रकार ढेर पर बनाए जाते हैं , बिना यह बताए कि ये दोनों चीजें क्या हैं। मैंने इसका स्पष्ट विवरण नहीं पढ़ा है। मैं समझता हूं कि एक स्टैक क्या है। परंतु,

  • वे कहाँ और क्या हैं (भौतिक रूप से एक वास्तविक कंप्यूटर की मेमोरी में)?
  • ओएस या भाषा रन-टाइम द्वारा उन्हें किस हद तक नियंत्रित किया जाता है?
  • उनका दायरा क्या है?
  • उनमें से प्रत्येक का आकार क्या निर्धारित करता है?
  • क्या एक तेज बनाता है?

175
वास्तव में अच्छी व्याख्या यहाँ मिल सकती है। ढेर और ढेर के बीच क्या अंतर है?
सोंगो

12
इसके अलावा (वास्तव में) अच्छा: codeproject.com/Articles/76153/… (ढेर / ढेर हिस्सा)
बेन


3
संबंधित, स्टैक क्लैश देखें । स्टैक क्लैश रिमेडियेशन ने सिस्टम चर और व्यवहार के कुछ पहलुओं को प्रभावित किया rlimit_stack। Red Hat अंक 1463241
jww

3
@mattshane स्टैक और हीप की परिभाषाएं मूल्य और संदर्भ प्रकारों पर निर्भर नहीं करती हैं। दूसरे शब्दों में, स्टैक और हीप को पूरी तरह से परिभाषित किया जा सकता है, भले ही मूल्य और संदर्भ प्रकार कभी मौजूद न हों। इसके अलावा, जब मूल्य और संदर्भ प्रकार को समझते हैं, तो स्टैक सिर्फ एक कार्यान्वयन विवरण है। प्रति एरिक लिपर्ट: द स्टैक इज एन इंप्लीमेंटेशन डिटेल, पार्ट वन
मैथ्यू

जवाबों:


5961

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

ढेर गतिशील आवंटन के लिए अलग सेट मेमोरी है। ढेर के विपरीत, ढेर से ब्लॉकों के आवंटन और निपटान के लिए कोई लागू पैटर्न नहीं है; आप किसी भी समय एक ब्लॉक आवंटित कर सकते हैं और इसे किसी भी समय मुक्त कर सकते हैं। यह बहुत अधिक जटिल बनाता है कि किसी भी समय ढेर के किन हिस्सों को आवंटित या मुफ्त किया जाए; विभिन्न उपयोग पैटर्न के लिए ढेर प्रदर्शन को ट्यून करने के लिए कई कस्टम हीप एलोकेटर उपलब्ध हैं।

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

अपने सवालों के सीधे जवाब देने के लिए:

ओएस या भाषा रनटाइम द्वारा उन्हें किस हद तक नियंत्रित किया जाता है?

जब ओएस बनाया जाता है तो ओएस प्रत्येक सिस्टम-स्तरीय थ्रेड के लिए स्टैक आवंटित करता है। आमतौर पर आवेदन के लिए ढेर आवंटित करने के लिए भाषा रनटाइम द्वारा ओएस को बुलाया जाता है।

उनका दायरा क्या है?

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

उनमें से प्रत्येक का आकार क्या निर्धारित करता है?

थ्रेड का आकार सेट किया जाता है जब एक थ्रेड बनाया जाता है। हीप का आकार एप्लिकेशन स्टार्टअप पर सेट किया गया है, लेकिन अंतरिक्ष की आवश्यकता के रूप में बढ़ सकता है (आवंटनकर्ता ऑपरेटिंग सिस्टम से अधिक मेमोरी का अनुरोध करता है)।

क्या एक तेज बनाता है?

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

एक स्पष्ट प्रदर्शन:
छवि स्रोत: vikashazrati.wordpress.com


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

274
मैं अंत में आरेख द्वारा वास्तव में भ्रमित हूं। मुझे लगा कि मुझे वह छवि मिलने तक मिल गई है।
सीना मदनी

10
@Anarelle प्रोसेसर ओएस के साथ या उसके बिना निर्देश चलाता है। मेरे दिल के करीब एक उदाहरण एसएनईएस है, जिसमें कोई एपीआई कॉल नहीं था, कोई ओएस नहीं जैसा कि हम आज जानते हैं - लेकिन यह एक स्टैक था। एक स्टैक पर आवंटन इसके अलावा और घटाव इन प्रणालियों पर है और यह तब नष्ट हो जाने वाले चर के लिए ठीक है, जब वे उस फ़ंक्शन से लौटकर पॉप होते हैं जो उन्हें बनाया था, लेकिन यह कहना कि एक निर्माता, जिसका निर्माण अभी नहीं हो सकता है दूर फेंका। उसके लिए हमें ढेर की जरूरत है, जो कॉल करने और लौटने के लिए बंधा नहीं है। अधिकांश OS में API का एक ढेर होता है, इसे अपने दम पर करने का कोई कारण नहीं है
15

2
"स्टैक स्क्रैच स्पेस के रूप में सेट मेमोरी है"। ठंडा। लेकिन यह वास्तव में जावा मेमोरी संरचना के संदर्भ में "अलग सेट" कहां है ?? क्या यह हीप मेमोरी / नॉन-हीप मेमोरी / अन्य ( betsol.com/2017/06/… के अनुसार जावा मेमोरी स्ट्रक्चर )
जतिन शशू

4
@JatinShashoo जावा रनटाइम, बायटेकोड दुभाषिया के रूप में, वर्चुअलाइजेशन के एक और स्तर को जोड़ता है, इसलिए आपको जो संदर्भित किया जाता है वह सिर्फ जावा अनुप्रयोग बिंदु है। ऑपरेटिंग सिस्टम के दृष्टिकोण से यह सब सिर्फ एक ढेर है, जहां जावा रनटाइम प्रक्रिया संसाधित बाईटेकोड के लिए अपने कुछ स्थान को "गैर-हीप" मेमोरी के रूप में आवंटित करती है। उस ओएस-लेवल हीप का उपयोग एप्लिकेशन-लेवल हीप के रूप में किया जाता है, जहां ऑब्जेक्ट का डेटा संग्रहीत किया जाता है।
kbec

2349

ढेर:

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

ढेर:

  • स्टैक की तरह कंप्यूटर रैम में संग्रहीत।
  • सी ++ में, ढेर पर चर को मैन्युअल रूप से नष्ट किया जाना चाहिए और कभी भी दायरे से बाहर नहीं होना चाहिए। डेटा के साथ मुक्त हो जाता है delete, delete[]या, free
  • स्टैक पर चर की तुलना में आवंटित करने के लिए धीमा।
  • कार्यक्रम द्वारा उपयोग के लिए डेटा का एक ब्लॉक आवंटित करने की मांग पर प्रयुक्त।
  • बहुत अधिक आवंटन और डील-डौल होने पर विखंडन हो सकता है।
  • C ++ या C में, ढेर पर बनाए गए डेटा को पॉइंटर्स द्वारा इंगित किया जाएगा और क्रमशः newया उसके साथ आवंटित किया जाएगा malloc
  • यदि बफर के बहुत बड़े को आवंटित करने का अनुरोध किया गया है तो आवंटन विफलताएं हो सकती हैं।
  • आप ढेर का उपयोग करेंगे यदि आपको ठीक से पता नहीं है कि आपको रन समय में कितने डेटा की आवश्यकता होगी या यदि आपको बहुत अधिक डेटा आवंटित करने की आवश्यकता है।
  • स्मृति लीक के लिए जिम्मेदार।

उदाहरण:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

31
सूचक pBuffer और b का मान स्टैक पर स्थित हैं, और फ़ंक्शन के प्रवेश द्वार पर ज्यादातर आवंटित किए जाते हैं। संकलक के आधार पर, फ़ंक्शन प्रवेश पर, साथ ही साथ बफर आवंटित किया जा सकता है।
एंडी

36
यह एक आम गलत धारणा है कि Cभाषा, C99भाषा के मानक ( ओपन-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf पर उपलब्ध है ) के रूप में परिभाषित होती है, जिसे "स्टैक" की आवश्यकता होती है। वास्तव में, 'स्टैक' शब्द मानक में भी दिखाई नहीं देता है। यह उत्तर देता है कि यह / के Cस्टैक उपयोग सामान्य रूप से सही है, लेकिन किसी भी तरह से भाषा के लिए आवश्यक नहीं है। अधिक जानकारी के लिए knosof.co.uk/cbook/cbook.html देखें , और विशेष रूप से कैसे Cविषम बॉल आर्किटेक्चर जैसे कि en.wikipedia.org/wiki/Burroughs_large_systems
johne

55
@ ब्रायन आपको यह बताना चाहिए कि क्यों बफर [] और पब्फ़ेर पॉइंटर को स्टैक पर बनाया गया है और क्यों पी बफ़र का डेटा ढेर पर बनाया गया है। मुझे लगता है कि कुछ ppl आपके जवाब से भ्रमित हो सकते हैं क्योंकि उन्हें लगता है कि कार्यक्रम विशेष रूप से निर्देश दे रहा है कि मेमोरी को ढेर बनाम ढेर पर आवंटित किया जाए लेकिन ऐसा नहीं है। क्या यह इसलिए है क्योंकि बफ़र एक मान प्रकार है जबकि pBuffer एक संदर्भ प्रकार है?
Howiecamp

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

35
"मेमोरी लीक के लिए जिम्मेदार" - मेमोरी लीक के लिए जिम्मेदार नहीं हैं! आलसी / विस्मृत / भूतपूर्व जावा कोडर्स / कोडर्स जो बकवास नहीं देते हैं!
लाज़

1370

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

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

    कागजों के ढेर की तरह ढेर

    एक स्टैक की सादगी यह है कि आपको आवंटित स्मृति के प्रत्येक अनुभाग के रिकॉर्ड वाली तालिका को बनाए रखने की आवश्यकता नहीं है; एकमात्र राज्य की जानकारी जो आपको चाहिए वह स्टैक के अंत में एक एकल सूचक है। आवंटित करने और डी-आवंटित करने के लिए, आप बस उस एकल सूचक को बढ़ाते और घटाते हैं। नोट: एक स्टैक को कभी-कभी मेमोरी के एक सेक्शन के शीर्ष पर शुरू करने के लिए लागू किया जा सकता है और ऊपर की तरफ बढ़ने के बजाय नीचे की तरफ बढ़ा सकता है।

  • ढेर में, जिस तरह से आइटम रखे जाते हैं, उसके लिए कोई विशेष आदेश नहीं है। आप किसी भी क्रम में आइटम तक पहुंच सकते हैं और हटा सकते हैं क्योंकि कोई स्पष्ट 'शीर्ष' आइटम नहीं है।

    नद्यपान allsorts के ढेर की तरह

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

इन छवियों को स्टैक और ढेर में मेमोरी को आवंटित करने और मुक्त करने के दो तरीकों का वर्णन करने का एक अच्छा काम करना चाहिए। यम!

  • ओएस या भाषा रनटाइम द्वारा उन्हें किस हद तक नियंत्रित किया जाता है?

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

    एक ढेर किसी भी स्मृति के लिए उपयोग किया जाने वाला एक सामान्य शब्द है जिसे गतिशील और यादृच्छिक रूप से आवंटित किया जाता है; यानी आउट ऑफ ऑर्डर। मेमोरी को आमतौर पर ओएस द्वारा आवंटित किया जाता है, इस आवंटन को करने के लिए एप्लिकेशन कॉलिंग एपीआई फ़ंक्शन के साथ। गतिशील रूप से आवंटित स्मृति के प्रबंधन में आवश्यक ओवरहेड का एक उचित बिट है, जिसे आमतौर पर उपयोग की जाने वाली प्रोग्रामिंग भाषा या पर्यावरण के रनटाइम कोड द्वारा नियंत्रित किया जाता है।

  • उनका दायरा क्या है?

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

  • उनमें से प्रत्येक का आकार क्या निर्धारित करता है?

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

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

  • क्या एक तेज बनाता है?

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


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

8
इस जवाब में एक बड़ी गलती शामिल है। स्टैटिक पर स्थैतिक चर आवंटित नहीं किए जाते हैं। स्पष्टीकरण के लिए मेरा उत्तर [लिंक] stackoverflow.com/a/13326916/1763801 देखें । आप "स्टैटिक" वैरिएबल के साथ "ऑटोमैटिक" वैरिएबल की बराबरी कर रहे हैं, लेकिन वे सभी समान नहीं हैं
davec

13
विशेष रूप से, आप कहते हैं कि "स्टैटिक पर आबंटित स्थानीय चर" आवंटित किए जाते हैं। दरअसल उन्हें डेटा सेगमेंट में आवंटित किया जाता है। केवल स्वचालित रूप से आवंटित चर (जिसमें अधिकांश नहीं बल्कि सभी स्थानीय चर शामिल हैं और संदर्भ के बजाय मान द्वारा पारित फ़ंक्शन पैरामीटर जैसी चीजें भी स्टैक पर आवंटित की जाती हैं)।
davec

9
मैंने महसूस किया है कि आप सही हैं - सी में, स्थैतिक आवंटन एक अलग चीज है जो गतिशील नहीं है । मैंने अपना उत्तर संपादित कर दिया है, धन्यवाद।
थोमसट्रेटर

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

727

(मैंने इस सवाल का जवाब एक और सवाल से लिया है, जो कमोबेश इसी का था।)

आपके प्रश्न का उत्तर विशिष्ट लागू हो रहा है और संकलक और प्रोसेसर आर्किटेक्चर में भिन्न हो सकता है। हालांकि, यहां एक सरलीकृत स्पष्टीकरण है।

  • स्टैक और ढेर दोनों अंतर्निहित ऑपरेटिंग सिस्टम से आवंटित मेमोरी क्षेत्र हैं (अक्सर वर्चुअल मेमोरी जो कि मांग पर भौतिक मेमोरी में मैप की जाती है)।
  • बहु-थ्रेडेड वातावरण में प्रत्येक थ्रेड का अपना पूर्ण स्वतंत्र स्टैक होगा, लेकिन वे ढेर साझा करेंगे। समवर्ती पहुंच को ढेर पर नियंत्रित किया जाना है और स्टैक पर संभव नहीं है।

ढेर

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

ढेर

ढेर

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

ढेर

क्या ढेर के बजाय ढेर पर एक कार्य आवंटित किया जा सकता है?

नहीं, फ़ंक्शंस (यानी स्थानीय या स्वचालित चर) के लिए सक्रियण रिकॉर्ड स्टैक पर आवंटित किए जाते हैं जो न केवल इन चर को संग्रहीत करने के लिए उपयोग किया जाता है, बल्कि नेस्टेड फ़ंक्शन कॉल का ट्रैक रखने के लिए भी उपयोग किया जाता है।

ढेर को कैसे प्रबंधित किया जाता है यह वास्तव में रनटाइम वातावरण तक है। C का उपयोग करता है mallocऔर C ++ का उपयोग करता है new, लेकिन कई अन्य भाषाओं में कचरा संग्रह है।

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


35
@ मर्टिन - अधिक अमूर्त स्वीकृत उत्तर की तुलना में बहुत अच्छा उत्तर / स्पष्टीकरण। स्टैक पॉइंटर्स / रजिस्टरों को दिखाने वाला एक नमूना असेंबली प्रोग्राम विज़ुअल फ़ंक्शन कॉल्स का उपयोग किया जा रहा है जो अधिक निराशाजनक होगा।
बिकल लेम

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

15
यह उत्तर मेरी राय में सबसे अच्छा था, क्योंकि इससे मुझे यह समझने में मदद मिली कि वास्तव में एक रिटर्न स्टेटमेंट क्या है और यह इस "रिटर्न एड्रेस" से कैसे संबंधित है कि मैं हर अब और फिर भर में आता हूं, स्टैक पर फ़ंक्शन को पुश करने का क्या मतलब है और फ़ंक्शन स्टैक पर क्यों धकेल दिए जाते हैं। बहुत बढ़िया जवाब!
एलेक्स

3
यह मेरी राय में सबसे अच्छा है, जिसका उल्लेख है कि ढेर / ढेर बहुत कार्यान्वयन विशिष्ट हैं। अन्य उत्तर भाषा और पर्यावरण / OS के बारे में बहुत सी बातें मानते हैं । +1
Qix - मोनासा ने 2

2
आपका क्या मतलब है "फ़ंक्शन में कोड इन मानों का पता लगाने के लिए वर्तमान स्टैक पॉइंटर से स्टैक को नेविगेट करने में सक्षम है।" ? क्या आप कृपया इसे विस्तार से बता सकते हैं?
कोरे तुगे

404

निम्नलिखित C # कोड में

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

यहां बताया गया है कि मेमोरी को कैसे प्रबंधित किया जाता है

ढेर पर चर की तस्वीर

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

ऑब्जेक्ट्स (जो हमें अपडेट करते समय आकार में भिन्न होते हैं) ढेर पर चलते हैं क्योंकि हम निर्माण समय पर नहीं जानते हैं कि वे कितने समय तक चलने वाले हैं। कई भाषाओं में ढेर वस्तुओं को खोजने के लिए इकट्ठा किया जाता है (जैसे कि cls1 ऑब्जेक्ट) जिसका अब कोई संदर्भ नहीं है।

जावा में, अधिकांश ऑब्जेक्ट सीधे हीप में जाते हैं। C / C ++ जैसी भाषाओं में, संरचित और वर्ग अक्सर स्टैक पर बने रह सकते हैं जब आप पॉइंटर्स के साथ काम नहीं कर रहे होते हैं।

अधिक जानकारी यहां पाई जा सकती है:

स्टैक और हीप मेमोरी आवंटन के बीच का अंतर «timmurphy.org

और यहाँ:

स्टैक और हीप पर ऑब्जेक्ट बनाना

यह लेख ऊपर दिए गए चित्र का स्रोत है: छह महत्वपूर्ण .NET अवधारणाएँ: ढेर, ढेर, मूल्य प्रकार, संदर्भ प्रकार, बॉक्सिंग और अनबॉक्सिंग - CodeProject

लेकिन ध्यान रखें कि इसमें कुछ गलतियाँ हो सकती हैं।


15
यह गलत है। i और cls "स्थिर" चर नहीं हैं। उन्हें "स्थानीय" या "स्वचालित" चर कहा जाता है। यह एक बहुत महत्वपूर्ण अंतर है। [कड़ी] देखें stackoverflow.com/a/13326916/1763801 स्पष्टीकरण के लिए
davec

9
मैंने यह नहीं कहा कि वे स्थिर चर थे । मैंने कहा कि int और cls1 स्टैटिक आइटम हैं । उनकी स्मृति को सांख्यिकीय रूप से आवंटित किया जाता है और इसलिए वे स्टैक पर जाते हैं। यह एक वस्तु के विपरीत है जिसमें गतिशील मेमोरी आवंटन की आवश्यकता होती है जो इसलिए हीप पर जाती है।
स्नोक्रैश

12
मैं बोली "स्टेटिक आइटम ... स्टैक पर चलते हैं"। यह गलत है। स्टैटिक आइटम डेटा सेगमेंट में जाते हैं, स्वचालित आइटम स्टैक पर जाते हैं।
davec

14
इसके अलावा जिसने भी लिखा कि codeproject लेख को पता नहीं है कि वह किस बारे में बात कर रहा है। उदाहरण के लिए, वह कहते हैं, "आदिम लोगों को स्थिर प्रकार की स्मृति की आवश्यकता होती है" जो पूरी तरह से असत्य है। डायनामिक रूप से ढेर में प्राइमिटिव्स को आवंटित करने से कुछ भी नहीं रुकता है, बस "int array [] = new int [num]" और वॉइला जैसा कुछ लिखिए, आदिमों को .NET में डायनामिक रूप से आवंटित किया जाता है। यह सिर्फ कई अशुद्धियों में से एक है।
davec

8
मैंने आपकी पोस्ट को संपादित किया क्योंकि आपने स्टैक और हीप में जाने के बारे में गंभीर तकनीकी गलतियाँ की हैं।
टॉम लीज

209

स्टैक जब आप किसी फ़ंक्शन को उस फ़ंक्शन के लिए तर्क कहते हैं, तो कुछ अन्य ओवरहेड को स्टैक पर रखा जाता है। कुछ जानकारी (जैसे कि वापसी पर कहाँ जाना है) भी वहाँ संग्रहीत है। जब आप अपने फ़ंक्शन के अंदर एक चर घोषित करते हैं, तो उस चर को स्टैक पर भी आवंटित किया जाता है।

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

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

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

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

यह केवल तभी व्यावहारिक है जब आपकी मेमोरी का उपयोग आदर्श से काफी अलग है - यानी उन खेलों के लिए जहां आप एक विशाल ऑपरेशन में एक स्तर को लोड करते हैं और दूसरे विशाल ऑपरेशन में पूरे बहुत दूर चक कर सकते हैं।

मेमोरी में भौतिक स्थान यह आपके द्वारा वर्चुअल मेमोरी नामक एक तकनीक के कारण आपके विचार से कम प्रासंगिक है जो आपके प्रोग्राम को लगता है कि आपके पास एक निश्चित पते तक पहुंच है जहां भौतिक डेटा कहीं और है (हार्ड डिस्क पर भी!)। स्टैक के लिए आपको मिलने वाले पते बढ़ते क्रम में हैं क्योंकि आपका कॉल ट्री गहरा हो जाता है। हीप के पते अन-प्रेडिक्टेबल (यानी विशिष्टार्थ) और स्पष्ट रूप से महत्वपूर्ण नहीं हैं।


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

2
यदि आप स्टैक या हीप का उपयोग कर सकते हैं, तो स्टैक का उपयोग करें। यदि आप स्टैक का उपयोग नहीं कर सकते हैं, तो वास्तव में कोई विकल्प नहीं है। मैं दोनों का बहुत उपयोग करता हूं, और निश्चित रूप से std :: वेक्टर या समान हिट का उपयोग करता है। एक नौसिखिए के लिए, आप ढेर से बचते हैं क्योंकि स्टैक बस इतना आसान है !!
टॉम ले

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

1
"यही कारण है कि ढेर से बचा जाना चाहिए (हालांकि यह अभी भी अक्सर उपयोग किया जाता है)।" मुझे यकीन नहीं है कि व्यावहारिक रूप से इसका क्या मतलब है, विशेष रूप से स्मृति को कई उच्च स्तरीय भाषाओं में अलग तरीके से प्रबंधित किया जाता है। जैसा कि यह प्रश्न भाषा-अज्ञेय को टैग किया गया है, मैं कहूंगा कि यह विशेष टिप्पणी / लाइन बीमार है और लागू नहीं है।
LintfordPickle

2
अच्छी बात @JonnoHampson - जब आप एक वैध बिंदु बनाते हैं, तो मैं यह तर्क दूंगा कि यदि आप GC के साथ "उच्च स्तरीय भाषा" में काम कर रहे हैं, तो शायद आप मेमोरी आवंटन तंत्र की बिल्कुल भी परवाह नहीं करते हैं - और इसलिए नहीं यहां तक ​​कि देखभाल क्या ढेर और ढेर हैं।
टॉम लेयस

194

स्पष्ट करने के लिए, इस उत्तर में गलत जानकारी है ( टिप्पणी, शांत :) के बाद थॉमस ने अपना उत्तर निर्धारित किया)। अन्य उत्तर केवल यह बताने से बचते हैं कि स्थैतिक आवंटन का क्या मतलब है। इसलिए मैं आवंटन के तीन मुख्य रूपों की व्याख्या करूँगा और वे आमतौर पर नीचे दिए गए ढेर, ढेर और डेटा खंड से कैसे संबंधित हैं। मैं लोगों को समझने में मदद करने के लिए C / C ++ और पायथन दोनों में कुछ उदाहरण दिखाऊंगा।

स्टैटिक पर "स्टेटिक" (AKA सांख्यिकीय रूप से आवंटित) चर आवंटित नहीं किए गए हैं। ऐसा मत मानो - बहुत से लोग केवल इसलिए करते हैं क्योंकि "स्टैटिक" बहुत कुछ "स्टैक" जैसा लगता है। वे वास्तव में न तो ढेर में मौजूद हैं और न ही ढेर। जिसे डेटा सेगमेंट कहा जाता है उसका हिस्सा हैं

हालांकि, आमतौर पर "स्टैक" और "हीप" के बजाय " स्कोप " और " लाइफटाइम " पर विचार करना बेहतर होता है ।

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

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

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

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

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

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

C / C ++ में कुछ वाक्यविन्यास विकल्प इस समस्या को बढ़ाते हैं - उदाहरण के लिए, बहुत से लोग सोचते हैं कि वैश्विक चर नीचे दिखाए गए वाक्यविन्यास के कारण "स्थिर" नहीं हैं।

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

ध्यान दें कि उपरोक्त घोषणा में "स्थिर" कीवर्ड को रखने से var2 वैश्विक दायरे में आने से रोकता है। फिर भी, वैश्विक संस्करण 1 में स्थैतिक आवंटन है। यह सहज नहीं है! इस कारण से, मैं गुंजाइश का वर्णन करते समय कभी भी "स्थिर" शब्द का उपयोग नहीं करने की कोशिश करता हूं, और इसके बजाय "फ़ाइल" या "फ़ाइल सीमित" क्षेत्र की तरह कुछ कहता हूं। हालाँकि, कई लोग "वैरिएबल" या "स्टैटिक स्कोप" वाक्यांश का उपयोग एक चर का वर्णन करने के लिए करते हैं जिसे केवल एक कोड फ़ाइल से एक्सेस किया जा सकता है। जीवन के संदर्भ में, "स्थिर" हमेशा मतलब है चर कार्यक्रम शुरू में आवंटित और पुनः आवंटित की जाती है जब कार्यक्रम बाहर निकलता है।

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

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones

मैं एक फ़ंक्शन के भीतर केवल स्थानीय पहुँच क्षमता के रूप में घोषित एक स्थिर चर का उल्लेख करूंगा , लेकिन आम तौर पर इसके साथ "गुंजाइश" शब्द का उपयोग नहीं किया जाएगा। इसके अलावा, यह ध्यान देने योग्य हो सकता है कि एक स्टैक / हीप पहलू जिसके साथ भाषाओं में अनिवार्य रूप से शून्य लचीलापन होता है: एक स्टैक पर निष्पादन के संदर्भ को सहेजने वाली भाषा उन स्टैक का उपयोग नहीं कर सकती है जो उन संदर्भों को रेखांकित करने की आवश्यकता होगी जिनमें वे बनाए गए हैं। । कुछ भाषाओं की तरह PostScriptकई ढेर हैं, लेकिन एक "ढेर" है जो एक स्टैक की तरह अधिक व्यवहार करता है।
सुपरकैट

@ सुपरकैट यह सब समझ में आता है। मैंने गुंजाइश को "के रूप में परिभाषित किया है कि कोड के कौन से हिस्से एक चर तक पहुंच सकते हैं " (और महसूस करें कि यह सबसे मानक परिभाषा है) इसलिए मुझे लगता है कि हम सहमत हैं :)
davec

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

@supercat यही कारण है कि मैं जीवन भर शब्द का उपयोग करता हूं, जो कि कैसे मैं आपको समय की गुंजाइश कहता हूं। यह इतने सारे अर्थों के साथ "गुंजाइश" शब्द को ओवरलोड करने की आवश्यकता को कम करता है। जहाँ तक मैं बता सकता हूँ, वहाँ सटीक परिभाषाओं पर कुल सहमति नहीं लगती है, हालांकि यहां तक ​​कि विहित स्रोतों के बीच भी। मेरी शब्दावली K & R से आंशिक रूप से तैयार की गई है और आंशिक रूप से पहले CS विभाग में जिसका मैंने अध्ययन किया / पढ़ाया जाता है, प्रचलित उपयोग से लिया है। हमेशा एक और सूचित दृश्य सुनने के लिए अच्छा है।
davec

1
आप मजाक कर रहे हो। क्या आप वास्तव में किसी फ़ंक्शन के अंदर स्थैतिक चर को परिभाषित कर सकते हैं?
ज़ीम सत्तार १६'१ar को

168

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

  1. स्टैक और हीप को एकवचन की आवश्यकता नहीं है। एक सामान्य स्थिति जिसमें आपके पास एक से अधिक स्टैक होते हैं यदि आपके पास एक प्रक्रिया में एक से अधिक थ्रेड हैं। इस मामले में प्रत्येक थ्रेड का अपना स्टैक होता है। आपके पास एक से अधिक ढेर भी हो सकते हैं, उदाहरण के लिए कुछ DLL कॉन्फ़िगरेशन के परिणामस्वरूप विभिन्न DLL अलग-अलग ढेर से आवंटित हो सकते हैं, यही वजह है कि आम तौर पर एक अलग पुस्तकालय द्वारा आवंटित स्मृति को जारी करना एक बुरा विचार है।

  2. सी में आप एलोका के उपयोग के माध्यम से चर लंबाई आवंटन का लाभ प्राप्त कर सकते हैं , जो स्टैक पर आवंटित करता है, आवंटन के विपरीत, जो ढेर पर आवंटित करता है। यह मेमोरी आपके रिटर्न स्टेटमेंट को जीवित नहीं रखेगी, लेकिन यह स्क्रैच बफर के लिए उपयोगी है।

  3. विंडोज पर एक बहुत बड़ा अस्थायी बफर बनाना जिसका आप ज्यादा इस्तेमाल नहीं करते हैं वह मुफ्त नहीं है। ऐसा इसलिए है क्योंकि कंपाइलर एक स्टैक जांच लूप उत्पन्न करेगा, जिसे हर बार कहा जाता है कि यह सुनिश्चित करने के लिए आपके फ़ंक्शन को दर्ज किया गया है कि स्टैक मौजूद है (क्योंकि विंडोज को स्टैक के बढ़ने की आवश्यकता होने पर पता लगाने के लिए आपके स्टैक के अंत में एक सिंगल गार्ड पेज का उपयोग किया जाता है। यदि आप स्टैक के अंत में एक पृष्ठ से अधिक मेमोरी का उपयोग करते हैं तो आप दुर्घटनाग्रस्त हो जाएंगे)। उदाहरण:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

पुन: "आवंटन के विपरीत": क्या आपका मतलब है "मॉलॉक के विपरीत"?
पीटर मोर्टेंसन

कितना पोर्टेबल है alloca?
पीटर मोर्टेंसन

@PeterMortensen यह POSIX नहीं है, पोर्टेबिलिटी की गारंटी नहीं है।
डॉन न्यूफेल्ड

135

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

स्टैक और हीप परंपरागत रूप से प्रक्रिया के वर्चुअल एड्रेस स्पेस के विपरीत छोर पर स्थित होते हैं। जब एक्सेस किया जाता है, तो कर्नेल द्वारा निर्धारित आकार तक (जो इसके साथ समायोजित किया जा सकता है setrlimit(RLIMIT_STACK, ...)) स्टैक स्वचालित रूप से बढ़ता है । जब स्मृति आबंटक को आमंत्रित करता है brk()या तो ढेर बढ़ता हैsbrk() सिस्टम कॉल को , भौतिक मेमोरी के अधिक पृष्ठों को प्रोसेस के वर्चुअल एड्रेस स्पेस में मैप करता है।

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


117

स्टैक क्या है?

स्टैक ऑब्जेक्ट्स का ढेर है, आमतौर पर एक जो बड़े करीने से व्यवस्थित है।

यहां छवि विवरण दर्ज करें

कंप्यूटिंग आर्किटेक्चर में ढेर स्मृति के क्षेत्र हैं जहां डेटा को अंतिम-प्रथम-आउट तरीके से जोड़ा या हटाया जाता है।
बहु-थ्रेडेड अनुप्रयोग में, प्रत्येक थ्रेड का अपना स्टैक होगा।

एक ढेर क्या है?

एक ढेर बेतरतीब ढंग से ढेर चीजों का एक संग्रह है।

यहां छवि विवरण दर्ज करें

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

दोनों साथ में

बहु-थ्रेडेड अनुप्रयोग में, प्रत्येक थ्रेड का अपना स्टैक होगा। लेकिन, सभी अलग-अलग धागे ढेर को साझा करेंगे।
क्योंकि विभिन्न थ्रेड्स ढेर-थ्रेडेड एप्लिकेशन में हीप को साझा करते हैं, इसका मतलब यह भी है कि थ्रेड्स के बीच कुछ समन्वय होना चाहिए ताकि वे हीप में मेमोरी के उसी टुकड़े को एक्सेस करने और हेरफेर करने की कोशिश न करें उसी समय।

कौन सा तेज है - ढेर या ढेर? और क्यों?

ढेर की तुलना में स्टैक बहुत तेज है।
यह उस तरह से है क्योंकि मेमोरी स्टैक पर आवंटित की जाती है।
स्टैक पर मेमोरी आवंटित करना स्टैक पॉइंटर को ऊपर ले जाने के समान सरल है।

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

जावा मेमोरी मॉडल

यहां छवि विवरण दर्ज करें

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


115

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

ढेर मेमोरी का एक हिस्सा है जो ऑपरेटिंग सिस्टम द्वारा एक एप्लिकेशन को दिया जाता है, आमतौर पर मॉलॉक जैसे एक syscall के माध्यम से। आधुनिक OSes पर यह मेमोरी उन पेजों का एक सेट है, जिन पर केवल कॉलिंग प्रक्रिया का उपयोग होता है।

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

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


2
यहां यह भी ध्यान देने योग्य है कि इंटेल स्टैक एक्सेस को विशेष रूप से अनुकूलित करता है, विशेष रूप से चीजें जैसे भविष्यवाणी करना कि आप किसी फ़ंक्शन से कहाँ लौटते हैं।
टॉम ले

113

मुझे लगता है कि कई अन्य लोगों ने आपको इस मामले पर ज्यादातर सही जवाब दिए हैं।

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


8
एक और नाइटिक- अधिकांश उत्तर (हल्के से) का अर्थ है कि Cभाषा द्वारा "स्टैक" का उपयोग आवश्यक है । यह एक सामान्य गलत धारणा है, हालांकि यह (अब तक) कार्यान्वयन C99 6.2.4 automatic storage duration objects(चर) के लिए प्रतिमान पर हावी है । वास्तव में, "स्टैक" शब्द C99भाषा के मानक में भी प्रकट नहीं होता है : open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
johne

[@ नीचे] आपके जवाब पर मेरी एक छोटी सी टिप्पणी है। इस प्रश्न के स्वीकृत उत्तर पर एक नज़र डालें । यह कहता है कि फ्री स्टोर सबसे अधिक संभवतया ढेर के समान है , हालांकि जरूरी नहीं है।
ओमरऑथमैन

91

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

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


कितना पोर्टेबल है alloca? उदाहरण के लिए, क्या यह विंडोज पर काम करता है? क्या यह केवल यूनिक्स जैसे ऑपरेटिंग सिस्टम के लिए है?
पीटर मोर्टेंसन

89

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

ढेर मेमोरी डायनेमिक मेमोरी आवंटन का क्षेत्र है जो स्पष्ट रूप से "नया" या "कॉल आवंटित" करता है। यह एक विशेष डेटा संरचना है जो विभिन्न आकारों की स्मृति के ब्लॉक और उनके आवंटन की स्थिति को ट्रैक कर सकती है।

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


[@TED] आपने क्यों कहा "कभी-कभी पैरामीटर स्टैक पर धकेल दिए जाते हैं"? मुझे पता है कि वे हमेशा से हैं। क्या आप अधिक विस्तार से बता सकते हैं?
ओमरऑथमैन

1
@OmarOthman - मैं कहता हूं कि क्योंकि यह पूरी तरह से आपके कंपाइलर / दुभाषिया के लेखक पर निर्भर है कि जब सबरूटीन कहा जाता है तो क्या होता है। क्लासिक फोरट्रान व्यवहार एक स्टैक का उपयोग नहीं करना है। कुछ भाषाएं पास-बाय-नाम जैसी विदेशी चीजों का समर्थन करती हैं, जो प्रभावी रूप से एक पाठीय प्रतिस्थापन है।
TED

83

विकीनेवर से।

ढेर

जब कोई फ़ंक्शन या कोई विधि किसी अन्य फ़ंक्शन को कॉल करती है, जो किसी अन्य फ़ंक्शन आदि को कॉल करती है, तो उन सभी फ़ंक्शन का निष्पादन तब तक निलंबित रहता है जब तक कि बहुत अंतिम फ़ंक्शन उसका मान वापस नहीं करता है।

निलंबित फ़ंक्शन कॉल की यह श्रृंखला स्टैक है, क्योंकि स्टैक (फ़ंक्शन कॉल) में तत्व एक दूसरे पर निर्भर करते हैं।

स्टैक अपवाद हैंडलिंग और थ्रेड निष्पादन में विचार करने के लिए महत्वपूर्ण है।

ढेर

ढेर केवल चर को स्टोर करने के लिए कार्यक्रमों द्वारा उपयोग की जाने वाली मेमोरी है। हीप के तत्व (चर) की एक दूसरे के साथ कोई निर्भरता नहीं है और हमेशा किसी भी समय यादृच्छिक रूप से एक्सेस किया जा सकता है।


"मुझे स्वीकार किए गए उत्तर बेहतर पसंद हैं क्योंकि यह और भी निम्न स्तर है।" यह बुरी बात है, अच्छी बात नहीं है।
9

54

ढेर

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

ढेर

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

50

ठीक है, बस और कम शब्दों में, उनका मतलब है आदेश दिया गया और आदेश नहीं दिया गया ...!

स्टैक : स्टैक आइटम में, चीजें एक-दूसरे के शीर्ष पर मिलती हैं, जिसका अर्थ है संसाधित होने के लिए तेज़ और अधिक कुशल होना ...!

इसलिए विशिष्ट आइटम को इंगित करने के लिए हमेशा एक इंडेक्स होता है, जो तेजी से प्रसंस्करण करने वाला होता है, वस्तुओं के बीच भी संबंध होता है! ...

ढेर : कोई आदेश नहीं, प्रसंस्करण धीमा होने वाला है और मूल्यों को किसी विशेष क्रम या सूचकांक के साथ एक साथ गड़बड़ कर दिया जाता है ... यादृच्छिक होते हैं और उनके बीच कोई संबंध नहीं होता है ... इसलिए निष्पादन और उपयोग का समय अलग-अलग हो सकता है ...

मैं यह दिखाने के लिए नीचे चित्र भी बनाता हूँ कि वे कैसे दिख सकते हैं:

यहां छवि विवरण दर्ज करें


49

संक्षेप में

स्टैक का उपयोग स्थिर मेमोरी आवंटन और डायनेमिक मेमोरी आवंटन के लिए एक ढेर के लिए किया जाता है, दोनों को कंप्यूटर की रैम में संग्रहीत किया जाता है।


विस्तार से

ढेर

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

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

अधिक यहाँ पाया जा सकता है


ढेर

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

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

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

स्टैक के विपरीत, ढेर पर बनाए गए चर आपके कार्यक्रम में कहीं भी, किसी भी फ़ंक्शन द्वारा सुलभ हैं। ढेर चर अनिवार्य रूप से दायरे में वैश्विक हैं।

अधिक यहाँ पाया जा सकता है


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

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

यहां छवि विवरण दर्ज करें

आप स्टैक का उपयोग कर सकते हैं यदि आप जानते हैं कि संकलन समय से पहले आपको कितना डेटा आवंटित करने की आवश्यकता है, और यह बहुत बड़ा नहीं है। आप हीप का उपयोग कर सकते हैं यदि आपको ठीक से पता नहीं है कि रनटाइम में आपको कितने डेटा की आवश्यकता होगी या यदि आपको बहुत अधिक डेटा आवंटित करने की आवश्यकता है।

बहु-थ्रेडेड स्थिति में प्रत्येक थ्रेड का अपना पूर्ण स्वतंत्र स्टैक होगा, लेकिन वे ढेर साझा करेंगे। स्टैक थ्रेड विशिष्ट है और हीप एप्लिकेशन विशिष्ट है। स्टैक अपवाद हैंडलिंग और थ्रेड निष्पादन में विचार करने के लिए महत्वपूर्ण है।

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

यहां छवि विवरण दर्ज करें

रन-टाइम पर, यदि एप्लिकेशन को अधिक हीप की आवश्यकता होती है, तो यह मेमोरी को फ्री मेमोरी से आवंटित कर सकता है और यदि स्टैक को मेमोरी की आवश्यकता होती है, तो यह एप्लिकेशन के लिए फ्री मेमोरी आवंटित मेमोरी से मेमोरी को आवंटित कर सकता है।

यहां तक कि, और अधिक विस्तार दिया जाता है यहाँ और यहाँ


अब आपके प्रश्न के उत्तर आते हैं

ओएस या भाषा रनटाइम द्वारा उन्हें किस हद तक नियंत्रित किया जाता है?

जब ओएस बनाया जाता है तो ओएस प्रत्येक सिस्टम-स्तरीय थ्रेड के लिए स्टैक आवंटित करता है। आमतौर पर आवेदन के लिए ढेर आवंटित करने के लिए भाषा रनटाइम द्वारा ओएस को बुलाया जाता है।

अधिक यहाँ पाया जा सकता है

उनका दायरा क्या है?

पहले से ही शीर्ष में दिया गया है।

"आप स्टैक का उपयोग कर सकते हैं यदि आप जानते हैं कि संकलन समय से पहले आपको कितना डेटा आवंटित करने की आवश्यकता है, और यह बहुत बड़ा नहीं है। आप हीप का उपयोग कर सकते हैं यदि आपको ठीक से पता नहीं है कि आपको रनटाइम या कितने डेटा की आवश्यकता होगी। आपको बहुत अधिक डेटा आवंटित करने की आवश्यकता है। "

यहां और अधिक पाया जा सकता है

उनमें से प्रत्येक का आकार क्या निर्धारित करता है?

जब थ्रेड बनाया जाता है तो स्टैक का आकार OS द्वारा सेट किया जाता है। हीप का आकार एप्लिकेशन स्टार्टअप पर सेट किया गया है, लेकिन यह अंतरिक्ष की आवश्यकता के रूप में बढ़ सकता है (आवंटनकर्ता ऑपरेटिंग सिस्टम से अधिक मेमोरी का अनुरोध करता है)।

क्या एक तेज बनाता है?

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

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

विवरण यहाँ से पाया जा सकता है


36

1980 के दशक में, UNIX ने बड़ी कंपनियों के साथ अपने स्वयं के रोल करने वाले बन्नीज़ की तरह प्रचार किया। एक्सॉन के पास एक के रूप में दर्जनों ब्रांड नाम इतिहास में खो गए थे। स्मृति को कैसे लागू किया गया था यह कई कार्यान्वयनकर्ताओं के विवेक पर था।

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

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

एक विशिष्ट मेमोरी ब्लॉक बीएसएस (शून्य मूल्यों का एक ब्लॉक) था जो गलती से एक निर्माता की पेशकश में शून्य नहीं था। एक अन्य डेटा था जिसमें प्रारंभिक मूल्य थे, जिसमें तार और संख्याएं शामिल थीं। एक तीसरा CODE था जिसमें CRT (C क्रम), मुख्य, कार्य और पुस्तकालय थे।

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

एक विशिष्ट 1980 के दशक की शैली UNIX C प्रोग्राम मेमोरी लेआउट



26

सेंट की एक जोड़ी: मुझे लगता है, यह स्मृति चित्रमय और अधिक सरल आकर्षित करने के लिए अच्छा होगा:

यह अधिक आसान समझ के लिए सरलीकरण के साथ प्रक्रिया स्मृति निर्माण की मेरी दृष्टि है


तीर - दिखाएं जहां स्टैक और ढेर बढ़ते हैं, प्रक्रिया स्टैक आकार की सीमा होती है, ओएस में परिभाषित होती है, आमतौर पर थ्रेड बनाने के लिए थ्रेड स्टैक के आकार की सीमा एपीआई में होती है। हीप आमतौर पर अधिकतम वर्चुअल मेमोरी आकार को सीमित करके, 32 बिट 2-4 जीबी के लिए उदाहरण के लिए।

इतना सरल तरीका: प्रक्रिया ढेर प्रक्रिया के लिए सामान्य है और अंदर के सभी थ्रेड्स, मैलोडोक () जैसी किसी चीज के साथ सामान्य स्थिति में मेमोरी आवंटन के लिए उपयोग करते हैं ।

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


23

चूँकि कुछ उत्तर नटपिटिंग के थे, मैं अपने घुन का योगदान करने जा रहा हूँ।

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

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

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

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

ध्यान दें कि मैंने कहा " आमतौर पर प्रति फ़ंक्शन एक अलग स्टैक होता है"। दोनों कर रहे हैं stackful और stackless couroutines के कार्यान्वयन। अधिकांश उल्लेखनीय स्टैकफुल C ++ कार्यान्वयन Boost.Coroutine और Microsoft PPL के हैं async/await। (हालांकि, सी ++ के फिर से शुरू होने वाले कार्य (उर्फ) asyncऔरawait "), जो सी ++ 17 के लिए प्रस्तावित थे, स्टैकलेस कॉरआउट का उपयोग करने की संभावना है।)

सी ++ मानक पुस्तकालय के लिए फाइबर प्रस्ताव आगामी है। इसके अलावा, कुछ तृतीय-पक्ष पुस्तकालय हैं । ग्रीन धागे पायथन और रूबी जैसी भाषाओं में बेहद लोकप्रिय हैं।


19

मेरे पास साझा करने के लिए कुछ है, हालांकि प्रमुख बिंदु पहले से ही कवर किए गए हैं।

ढेर

  • बहुत तेज पहुंच।
  • रैम में संग्रहीत।
  • फंक्शन कॉल को स्थानीय चर और फ़ंक्शन मापदंडों के साथ यहां लोड किया गया है।
  • जब कार्यक्रम एक दायरे से बाहर हो जाता है तो अंतरिक्ष अपने आप मुक्त हो जाता है।
  • अनुक्रमिक मेमोरी में संग्रहीत।

ढेर

  • स्टैक तक तुलनात्मक रूप से धीमी पहुंच।
  • रैम में संग्रहीत।
  • गतिशील रूप से बनाए गए चर यहां संग्रहीत किए जाते हैं, जिन्हें बाद में उपयोग के बाद आवंटित मेमोरी को मुक्त करने की आवश्यकता होती है।
  • मेमोरी का आवंटन जहां भी किया जाता है, वहां हमेशा पॉइंटर द्वारा पहुँचा जाता है।

दिलचस्प नोट:

  • क्या फ़ंक्शन कॉल को ढेर में संग्रहीत किया गया था, इसके परिणामस्वरूप 2 गन्दे अंक थे:
    1. स्टैक में अनुक्रमिक भंडारण के कारण, निष्पादन तेज है। ढेर में भंडारण के परिणामस्वरूप भारी समय की खपत होती है, जिससे पूरे कार्यक्रम का निष्पादन धीमा हो जाता है।
    2. यदि फ़ंक्शंस को ढेर में संग्रहीत किया गया था (सूचक द्वारा इंगित गन्दा भंडारण), तो कॉलर एड्रेस वापस करने का कोई तरीका नहीं होता (जो स्टैक मेमोरी में अनुक्रमिक भंडारण के कारण देता है)।

1
संक्षिप्त और साफ। अच्छा :)
११

13

वाह! इतने सारे जवाब और मुझे नहीं लगता कि उनमें से एक ने इसे सही पाया ...

1) वे कहाँ और क्या हैं (शारीरिक रूप से एक वास्तविक कंप्यूटर की मेमोरी में)?

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

दो ढेर हैं: सार्वजनिक और निजी।

निजी ढेर 16-बाइट सीमा (64-बिट प्रोग्राम के लिए) या 8-बाइट सीमा (32-बिट प्रोग्राम के लिए) आपके प्रोग्राम में कोड के अंतिम बाइट के बाद शुरू होता है, और फिर वहां से मूल्य में वृद्धि होती है। इसे डिफॉल्ट हीप भी कहा जाता है।

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

सार्वजनिक हीप आपके प्रोग्राम इमेज स्पेस के बाहर इसकी मेमोरी स्पेस में रहता है। यह स्मृति है कि हार्ड डिस्क पर बंद हो जाएगा अगर स्मृति संसाधन दुर्लभ हो जाते हैं।

2) वे ओएस या भाषा रनटाइम द्वारा किस सीमा तक नियंत्रित होते हैं?

स्टैक प्रोग्रामर द्वारा नियंत्रित किया जाता है, निजी ढेर ओएस द्वारा प्रबंधित किया जाता है, और सार्वजनिक हीप किसी के द्वारा नियंत्रित नहीं किया जाता है क्योंकि यह ओएस सेवा है - आप अनुरोध करते हैं और या तो उन्हें अनुमति दी जाती है या इनकार कर दिया जाता है।

2 बी) उनका दायरा क्या है?

वे सभी कार्यक्रम के लिए वैश्विक हैं, लेकिन उनकी सामग्री निजी, सार्वजनिक या वैश्विक हो सकती है।

2c) उनमें से प्रत्येक का आकार क्या निर्धारित करता है?

स्टैक का आकार और निजी ढेर आपके कंपाइलर रनटाइम विकल्पों द्वारा निर्धारित किए जाते हैं। सार्वजनिक हीप को आकार पैरामीटर का उपयोग करके रनटाइम पर आरंभीकृत किया जाता है।

2d) क्या एक तेज बनाता है?

उन्हें तेजी से डिजाइन नहीं किया गया है, वे उपयोगी होने के लिए डिज़ाइन किए गए हैं। प्रोग्रामर उनका उपयोग कैसे करता है यह निर्धारित करता है कि वे "तेज" हैं या "धीमी"

संदर्भ:

https://norasandler.com/2019/02/18/Write-a-Compiler-10.html

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate


8

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

स्टैक पर आप रिटर्न एड्रेस सेव करते हैं और कॉल करते हैं → पुश / रिट → पॉप को सीधे हार्डवेयर में मैनेज किया जाता है।

आप मापदंडों को पारित करने के लिए स्टैक का उपयोग कर सकते हैं .. भले ही यह रजिस्टरों का उपयोग करने की तुलना में धीमा हो (क्या एक माइक्रोप्रोसेसर गुरु का कहना है या एक अच्छा 1980 के दशक की BIOS पुस्तक ...)

  • बिना स्टैक नं माइक्रोप्रोसेसर काम कर सकता है। (हम सबरूटीन्स / फंक्शन्स के बिना, असेंबली लैंग्वेज में भी प्रोग्राम की कल्पना नहीं कर सकते हैं)
  • ढेर के बिना यह कर सकते हैं। (असेम्बली लैंग्वेज प्रोग्राम बिना काम कर सकता है, क्योंकि हीप एक OS कॉन्सेप्ट है, जैसा कि मॉलोक, यानी OS / Lib कॉल।

स्टैक का उपयोग इस प्रकार है:

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

ओपीपी क्या है? क्या आपका मतलब OOP ( ऑब्जेक्ट ओरिएंटेड_प्रोग्रामिंग ) है?
पीटर मोर्टेंसन

क्या आपके कहने का मतलब है कि mallocकर्नेल कॉल है?
पीटर मोर्टेंसन

1) हाँ, क्षमा करें .. OOP ... 2) मॉलोक: मैं शीघ्र ही लिखता हूँ, क्षमा करें ... मॉलोक उपयोगकर्ता स्थान में है .. लेकिन अन्य कॉल को ट्रिगर कर सकता है .... मुद्दा यह है कि हीप का उपयोग करना बहुत धीमा हो सकता है। ...
११:

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

1

स्टैक अनिवार्य रूप से एक आसान-से-एक्सेस मेमोरी है जो बस अपने आइटम को एक अच्छी तरह से स्टैक के रूप में प्रबंधित करता है। केवल वे आइटम जिनके लिए आकार पहले से ज्ञात है, वे स्टैक पर जा सकते हैं । यह संख्या, तार, बूलियन के लिए मामला है।

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

स्रोत: एकेडमाइंड


0

वास्तव में अच्छी चर्चा के लिए धन्यवाद, लेकिन एक वास्तविक noob के रूप में मुझे आश्चर्य है कि निर्देश कहाँ रखे गए हैं? BEGINNING में वैज्ञानिक दो आर्किटेक्चर (वॉन NEUMANN जहां सब कुछ DATA और HARVARD माना जाता है, के बीच तय कर रहे थे जहां मेमोरी का एक क्षेत्र निर्देशों के लिए और दूसरा डेटा के लिए आरक्षित था)। अंततः, हम वॉन न्यूमैन डिज़ाइन के साथ गए और अब सब कुछ 'एक ही' माना जाता है। यह मेरे लिए कठिन हो गया जब मैं विधानसभा सीख रहा था https://www.cs.virginia.edu/~evans/cs216/guides/x86.html क्योंकि वे रजिस्टरों और स्टैक पॉइंटर्स के बारे में बात करते हैं।

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

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