क्या यह संभव है कि स्‍मृति का अनुमान लगाया जाए कि स्‍मृति को कब स्‍पष्‍ट किया जाए --- केवल स्रोत कोड से?


27

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

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

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

ऐसा लगता है कि एक कार्यक्रम लिखना संभव है जो एक कार्यक्रम के स्रोत को पढ़ सकता है और:

  1. कार्यक्रम के नियंत्रण निष्पादन के सभी क्रमपरिवर्तन की भविष्यवाणी करें --- कार्यक्रम की लाइव निष्पादन देखने के समान सटीकता के लिए
  2. आवंटित संसाधनों के लिए सभी संदर्भों को ट्रैक करें
  3. प्रत्येक संदर्भ के लिए, पूरे बाद के नियंत्रण प्रवाह को पीछे हटा दें ताकि यह पता चल सके कि संदर्भ की गारंटी कभी नहीं दी जाएगी।
  4. उस बिंदु पर, सोर्स कोड की उस लाइन पर एक डिक्लोकेशन स्टेटमेंट डालें

वहाँ कुछ भी है कि यह पहले से ही है? मुझे नहीं लगता कि Rust या C ++ स्मार्ट पॉइंटर्स / RAII एक ही बात है।


57
रुकने की समस्या को देखें। यह दादाजी का सवाल है "अगर एक प्रोग्राम एक्स करता है तो एक संकलक का पता नहीं चल सकता है?" हमेशा "सामान्य मामले में नहीं" के साथ उत्तर दिया जाता है।
शाफ़्ट ने

18
मेमोरी (और संसाधन लॉक) एक कार्यक्रम के निष्पादन के दौरान निर्धारक बिंदुओं पर ओएस पर वापस आ जाते हैं। संख्या
युफोरिक

9
@ratchetfreak धन्यवाद, यह कभी भी इस समस्या को हल करने जैसी चीज़ों को नहीं जानता है, जिससे मुझे लगता है कि मुझे रसायन विज्ञान के बजाय COMP विज्ञान में मेरी डिग्री मिल गई है।
zelcon 12

15
@ zelcon5, अब आप केमिस्ट्री और रुकने की समस्या के बारे में जानते हैं ... :)
डेविड अर्नो

7
@ उत्साही जब तक आप अपने कार्यक्रम की संरचना नहीं करते हैं, जब संसाधन का उपयोग करने की सीमा बहुत स्पष्ट है जैसे कि RAII या कोशिश के साथ संसाधन
शाफ़्ट सनकी

जवाबों:


23

इसे ले लीजिए (उदाहरण के लिए):

void* resource1;
void* resource2;

while(true){

    int input = getInputFromUser();

    switch(input){
        case 1: resource1 = malloc(500); break;
        case 2: resource2 = resource1; break;
        case 3: useResource(resource1); useResource(resource2); break;
    }
}

मुक्त कब कहा जाना चाहिए? मॉलॉक से पहले और resource1हम करने के लिए असाइन नहीं कर सकते क्योंकि इसे कॉपी किया जा सकता है resource2, resource2हम असाइन करने से पहले नहीं कर सकते क्योंकि हम एक हस्तक्षेप 1 के बिना दो बार उपयोगकर्ता से 2 प्राप्त कर सकते हैं।

यह सुनिश्चित करने का एकमात्र तरीका संसाधन 1 और संसाधन 2 का परीक्षण करना है कि क्या वे 1 और 2 के मामलों में समान नहीं हैं और यदि वे नहीं थे तो पुराने मूल्य को मुक्त कर सकते हैं। यह अनिवार्य रूप से संदर्भ की गिनती है जहां आप जानते हैं कि केवल 2 संभावित संदर्भ हैं।


वास्तव में यह एकमात्र तरीका नहीं है; दूसरी तरह के केवल करने के लिए है की अनुमति देने के एक अस्तित्व के लिए नकल। यह, निश्चित रूप से, अपनी समस्याओं के साथ आता है।
जैक एडले

27

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

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

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


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
maple_shaft

13

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

क्षेत्र-आधारित मेमोरी प्रबंधन पर एक पूर्वव्यापी एमएल किट के मूल लेखकों द्वारा एक लेख है जो इसकी सफलताओं और विफलताओं में जाता है। अंतिम निष्कर्ष यह है कि ढेर प्रोफाइलर की सहायता से लिखते समय रणनीति व्यावहारिक है।

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


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

ध्यान दें कि समस्या तब और अधिक हलकी हो जाती है जब हम शुद्ध या लगभग शुद्ध कार्यात्मक, गैर-साइड-इफ़ेक्टिंग भाषाओं जैसे स्टैंडर्ड एमएल और हास्केल के बारे में बात करते हैं
बिल्ली

10

कार्यक्रम के नियंत्रण प्रवाह के सभी क्रमपरिवर्तन की भविष्यवाणी करें

समस्या यहीं है। किसी भी गैर-तुच्छ कार्यक्रम के लिए क्रमपरिवर्तन की मात्रा बहुत बड़ी है (व्यवहार में यह अनंत है), उस समय और आवश्यक स्मृति इसे पूरी तरह से अव्यवहारिक बना देगी।


अच्छी बात। मुझे लगता है कि क्वांटम प्रोसेसर एकमात्र उम्मीद है, अगर कोई भी है
zelcon

4
@ zelcon5 हाहा, नहीं। क्वांटम कंप्यूटिंग इसे बदतर बनाती है , बेहतर नहीं। यह प्रोग्राम में अतिरिक्त ("छिपा हुआ") चर जोड़ता है और बहुत अधिक अनिश्चितता है। अधिकांश व्यावहारिक क्यूसी कोड मैंने देखा है "तेजी से गणना के लिए क्वांटम, पुष्टि के लिए शास्त्रीय"। मैंने बड़ी मुश्किल से क्वांटम कंप्यूटिंग की सतह पर खुद को स्क्रैच किया है, लेकिन मुझे ऐसा लगता है कि क्वांटम कंप्यूटर शास्त्रीय कंप्यूटरों के बिना बहुत उपयोगी नहीं हो सकते हैं ताकि उन्हें वापस किया जा सके और उनके परिणामों की जांच की जा सके।
लुआॅन

8

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

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


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

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

ठीक है, आपने "लगभग सभी संकलक" वाक्यांश सहित एक बहुत ही सामान्य वक्तव्य दिया है जिसमें सी संकलक शामिल होना चाहिए।
पीटर -

2
सी कंपाइलर इसका उपयोग यह निर्धारित करने के लिए करते हैं कि रजिस्टरों को क्या अस्थायी चर आवंटित किए जा सकते हैं।
कार्ल ब्वेलफेल्ट

4

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

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

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

कुछ विकल्प (कुछ उभरते) हैं।

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

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


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

2

यदि कोई प्रोग्राम किसी अज्ञात इनपुट पर निर्भर नहीं करता है, तो हाँ, यह संभव होना चाहिए (कैविट के साथ कि यह एक जटिल कार्य हो सकता है और इसमें लंबा समय लग सकता है; लेकिन यह प्रोग्राम के लिए भी सही होगा)। ऐसे कार्यक्रम संकलन समय पर पूरी तरह से हल किए जाएंगे; C ++ शब्दों में, वे (लगभग) पूरी तरह से constexprs से बना हो सकता है । सरल उदाहरण पीआई के पहले 100 अंकों की गणना करना या किसी ज्ञात शब्दकोश को क्रमबद्ध करना होगा।


2

स्मृति को मुक्त करना, सामान्य रूप से, हॉल्टिंग समस्या के बराबर है - यदि आप स्टेटिक रूप से यह नहीं बता सकते हैं कि कोई प्रोग्राम रुक जाएगा (स्टेटिक रूप से), तो आप यह नहीं बता सकते कि यह मेमोरी (स्टैटिकली) फ्री होगी या नहीं।

function foo(int a) {
    void *p = malloc(1);
    ... do something which may, or may not, halt ...
    free(p);
}

https://en.wikipedia.org/wiki/Halting_problem

उस ने कहा, जंग बहुत अच्छी है ... https://doc.rust-lang.org/book/ownership.html

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