मेमोरी आवंटन: ढेर बनाम ढेर?


84

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

अब निम्नलिखित उदाहरण पर विचार करें:

class MyClass
{
    int myInt = 0;    
    string myString = "Something";
}

class Program
{
    static void Main(string[] args)
    {
       MyClass m = new MyClass();
    }
}

अब, मेमोरी आवंटन c # में कैसे होगा? क्या MyClass(यानी m) का उद्देश्य पूरी तरह से हीप को आवंटित किया जाएगा? यही है, int myIntऔर string myStringदोनों ढेर जाएंगे?

या, ऑब्जेक्ट को दो भागों में विभाजित किया जाएगा और दोनों को उन मेमोरी स्थानों पर आवंटित किया जाएगा जो स्टैक और हीप हैं?


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

क्या इससे आपके सवाल का जवाब मिलता है? क्या और कहाँ ढेर और ढेर हैं?
ओलिवियर रोजियर

जवाबों:


55

mढेर पर आवंटित किया जाता है, और इसमें शामिल है myInt। उन स्थितियों पर जहां आदिम प्रकार (और संरचनाएं) स्टैक पर आवंटित की जाती हैं, विधि आह्वान के दौरान होती है, जो स्टैक पर स्थानीय चर के लिए कमरा आवंटित करती है (क्योंकि यह तेज है)। उदाहरण के लिए:

class MyClass
{
    int myInt = 0;

    string myString = "Something";

    void Foo(int x, int y) {
       int rv = x + y + myInt;
       myInt = 2^rv;
    }
}

rv, x, yसभी ढेर पर होगा। myIntढेर पर कहीं है (और thisसूचक के माध्यम से पहुंच होनी चाहिए )।


7
एक महत्वपूर्ण परिशिष्ट यह याद रखना है कि "स्टैक" और "हीप" वास्तव में .NET में कार्यान्वयन विवरण हैं। सी # का कानूनी क्रियान्वयन करना पूरी तरह से संभव है जो स्टैक-आधारित आवंटन का उपयोग बिल्कुल नहीं करता है।
JSB JS

5
मैं मानता हूं कि उन्हें इस तरह से व्यवहार किया जाना चाहिए , लेकिन यह पूरी तरह सच नहीं है कि वे विशुद्ध रूप से कार्यान्वयन विवरण हैं। इसे सार्वजनिक एपीआई प्रलेखन और भाषा मानक (EMCA-334, ISO / IEC 23270: 2006) में स्पष्ट रूप से नोट किया गया है। ") लेकिन, हाँ, यदि ढेर आवंटन की गति आपके आवेदन के लिए एक अड़चन है, तो आप शायद इसे गलत कर रहे हैं (या गलत भाषा का उपयोग कर रहे हैं)।
मड

65

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

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

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

क्या मुझे कुछ याद है?

अगर मैं इस विषय पर एरिक लिपर्ट के पोस्ट से लिंक नहीं करता, तो निश्चित रूप से मुझे याद होगा:


1
Ed: वास्तव में जब यह बात करता है?
गाबे

1
@ गैबे: यह मायने रखता है कि बिट्स कहाँ संग्रहीत हैं। उदाहरण के लिए, यदि आप किसी क्रैश डंप को डिबग कर रहे हैं, तो आप बहुत दूर नहीं जा सकते हैं जब तक कि आप नहीं जानते कि वस्तुओं / डेटा को कहां देखें।
ब्रायन रासमुसेन

14
आपके द्वारा छोड़ी गई स्थितियां हैं: यदि मान प्रकार एक असुरक्षित पॉइंटरथेन के माध्यम से एक्सेस किए गए अप्रबंधित कोड से है, तो संभवतः यह न तो स्टैक और न ही प्रबंधित हीप पर है। यह अप्रबंधित हीप पर हो सकता है, या कुछ डेटा संरचना में जो कि ढेर भी नहीं है। पूरा विचार है कि "ढेर" एक मिथक भी है। दर्जनों ढेर हो सकते हैं। इसके अलावा, अगर घबराना मूल्य को कम करने का विकल्प चुनता है तो यह ढेर या ढेर पर नहीं है, यह एक रजिस्टर में है।
एरिक लिपर्ट

1
एरिक लिपर्ट के भाग दो एक शानदार पढ़ा था, लिंक के लिए धन्यवाद!
दान बेचर

2
यह मायने रखता है क्योंकि यह साक्षात्कार में पूछा जाता है लेकिन वास्तविक जीवन में नहीं। :)
मयंक

23

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

यहां तक ​​कि विधि चर भी ढेर पर हो सकते हैं, यदि वे कैप्चर किए गए हैं (लंबो / एनॉन-विधि), या एक इटैलर ब्लॉक का हिस्सा (उदाहरण के लिए)।


1
और मुक्केबाजी को मत भूलना: यदि आपके पास object x = 12;एक विधि है, तो 12 ढेर पर संग्रहीत हो जाएगा, भले ही यह पूर्णांक (एक मूल्य प्रकार) हो।
गब्बर

@Gabe: मूल्य-प्रकार के संग्रहण स्थान अपने आप में एक मूल्य प्रकार के फ़ील्ड (सार्वजनिक और निजी) रखते हैं। संदर्भ-प्रकार के भंडारण स्थान या तो पकड़ते हैं null, या उपयुक्त प्रकार के ढेर ऑब्जेक्ट का संदर्भ। प्रत्येक मूल्य प्रकार के लिए एक समान ढेर-वस्तु प्रकार है; एक संदर्भ प्रकार के भंडारण स्थान में एक मूल्य प्रकार को संग्रहीत करने का प्रयास करने से इसके संबंधित हीप-ऑब्जेक्ट प्रकार की एक नई वस्तु का उत्पादन होगा, सभी फ़ील्ड को उस नई ऑब्जेक्ट पर कॉपी करें, और संदर्भ-प्रकार भंडारण स्थान में ऑब्जेक्ट के संदर्भ को संग्रहीत करें। C # मान प्रकार का दिखावा करता है और ऑब्जेक्ट प्रकार समान होते हैं, लेकिन ...
सुपरकैट

... ऐसा दृष्टिकोण समझने के बजाय भ्रम जोड़ता है। एक अनबॉक्स List<T>.Enumeratorजो उस प्रकार के एक चर में संग्रहित होता है, वह मूल्य शब्दार्थ प्रदर्शित करेगा, क्योंकि यह एक मूल्य प्रकार है। एक List<T>.Enumeratorकिस प्रकार के एक चर में संग्रहीत किया जाता IEnumerator<T>है, तथापि, एक संदर्भ प्रकार की तरह व्यवहार करेगा। यदि कोई पूर्व से भिन्न प्रकार के उत्तरार्द्ध को मानता है, तो व्यवहार में अंतर आसानी से समझा जा सकता है। यह कहते हुए कि वे एक ही प्रकार के हैं, उनके बारे में तर्क करना बहुत कठिन है।
सुपरकैट

12

2

ढेर

stackभंडारण के लिए स्मृति का एक ब्लॉक है local variablesऔर parameters। स्टैक तार्किक रूप से बढ़ता है और एक फ़ंक्शन में प्रवेश और बाहर निकलने के लिए सिकुड़ता है।

निम्नलिखित विधि पर विचार करें:

public static int Factorial (int x)
{
    if (x == 0) 
    {
        return 1;
    }

    return x * Factorial (x - 1);
}

यह विधि पुनरावर्ती है, जिसका अर्थ है कि यह खुद को कहता है। हर बार जब विधि दर्ज की जाती है, तो स्टैक पर एक नया इंट आवंटित किया जाता है , और हर बार विधि से बाहर निकलने के बाद, इंट को हटा दिया जाता है


ढेर

  • ढेर स्मृति का एक खंड है जिसमें objects(यानी, reference-type instances) निवास करता है। जब भी कोई नई वस्तु बनाई जाती है, तो उसे ढेर पर आवंटित किया जाता है, और उस वस्तु का एक संदर्भ वापस कर दिया जाता है। एक कार्यक्रम के निष्पादन के दौरान, नई वस्तुओं का निर्माण होते ही ढेर भरना शुरू हो जाता है। रनटाइम में एक कचरा संग्राहक होता है जो समय-समय पर ढेर से वस्तुओं को हटाता है, इसलिए आपका प्रोग्राम नहीं चलता है Out Of Memory। एक वस्तु सौदा करने के लिए योग्य है जैसे ही यह किसी भी चीज से संदर्भित नहीं है जो स्वयं है alive
  • ढेर भी संग्रहीत करता है static fields। ढेर पर आवंटित वस्तुओं के विपरीत (जो कचरा एकत्र कर सकते हैं) these live until the application domain is torn down,।

निम्नलिखित विधि पर विचार करें:

using System;
using System.Text;

class Test
{
    public static void Main()
    {
        StringBuilder ref1 = new StringBuilder ("object1");
        Console.WriteLine (ref1);
        // The StringBuilder referenced by ref1 is now eligible for GC.

        StringBuilder ref2 = new StringBuilder ("object2");
        StringBuilder ref3 = ref2;
        // The StringBuilder referenced by ref2 is NOT yet eligible for GC.
        Console.WriteLine (ref3); // object2
    }
}    

उपरोक्त उदाहरण में, हम चर Ref1 द्वारा संदर्भित StringBuilder वस्तु बनाकर शुरू करते हैं, और फिर इसकी सामग्री लिखते हैं। वह स्ट्रिंगबुलस्ट वस्तु तब कचरा संग्रहण के लिए तुरंत योग्य है, क्योंकि बाद में कुछ भी इसका उपयोग नहीं करता है। फिर, हम वैरिएबल Ref2 द्वारा संदर्भित एक और StringBuilder बनाते हैं, और उस संदर्भ को Ref3 में कॉपी करते हैं। हालांकि उस बिंदु के बाद Ref2 का उपयोग नहीं किया जाता है, फिर भी Ref3 उसी StringBuilder वस्तु को जीवित रखता है - यह सुनिश्चित करते हुए कि यह संग्रह के लिए योग्य नहीं है जब तक कि हम ref3 का उपयोग करके समाप्त नहीं हो जाते।

जहाँ भी वैरिएबल घोषित किया गया था, वहाँ वैल्यू-टाइप इंस्टेंस (और ऑब्जेक्ट संदर्भ) रहते हैं। यदि उदाहरण को एक वर्ग प्रकार के भीतर या एक सरणी तत्व के रूप में क्षेत्र के रूप में घोषित किया गया था, तो वह उदाहरण ढेर पर रहता है।


1

सरल उपाय

मूल्य प्रकार को स्टैक पर भेजा जा सकता है, यह कार्यान्वयन संबंधी विवरण है जिसे इसे कुछ भविष्यवादी डेटा संरचना को आवंटित किया जा सकता है।

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

संदर्भ प्रकारों को संदर्भ द्वारा पास किया जाता है (सहमत नहीं मानते कि संदर्भ भविष्य के कुछ संस्करणों में एक पते को फिर से संग्रहीत करेगा, इसे कुछ अन्य डेटा संरचनाओं पर संग्रहीत किया जा सकता है।)

आपके मामले में ऐसा है

myInt एक ऐसा उदाहरण है जिसे एक वर्ग में रखा गया है जो एक संदर्भ प्रकार को ऑफ़कोर्स करता है इसलिए इसे उस वर्ग के उदाहरण से जोड़ा जाएगा जिसे 'THE HEAP' पर संग्रहीत किया जाएगा।

मेरा सुझाव है, आप ERIC LIPPERTS द्वारा लिखे गए ब्लॉग पढ़ना शुरू कर सकते हैं।

एरिक का ब्लॉग


1

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

यह लिंक उपयोगी भी है http://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/


0

मी MyClass की एक ऑब्जेक्ट का संदर्भ है इसलिए m मुख्य धागे के ढेर में स्टोर है लेकिन ढेर में MyClass स्टोर की वस्तु। इसलिए ढेर में myInt और myString स्टोर। ध्यान दें कि मी केवल एक संदर्भ (मेमोरी के लिए एक पता) है और मुख्य स्टैक पर है। जब एम डील किया गया तो GC ने ढेर से MyClass ऑब्जेक्ट को साफ़ कर दिया। अधिक विवरण के लिए इस लेख के सभी चार भागों को पढ़ें https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net- भाग- i /

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