C ++ में कचरा कलेक्टर क्यों नहीं है?


270

सबसे पहले कचरा संग्रह के गुण के कारण मैं यह सवाल नहीं पूछ रहा हूं। यह पूछने का मेरा मुख्य कारण यह है कि मुझे पता है कि बज़्ने स्ट्रॉस्ट्रुप ने कहा है कि C ++ में कुछ समय में एक कचरा संग्रहकर्ता होगा।

इसके साथ ही कहा, इसे क्यों नहीं जोड़ा गया? C ++ के लिए पहले से ही कुछ कचरा संग्रहकर्ता हैं। क्या यह केवल उन चीजों में से एक है जो "टाइप की गई चीजों की तुलना में आसान" हैं? या अन्य कारण हैं जो इसे जोड़ा नहीं गया है (और C ++ 11 में नहीं जोड़ा जाएगा)?

क्रॉस लिंक:

बस स्पष्ट करने के लिए, मैं उन कारणों को समझता हूं कि जब इसे पहली बार बनाया गया था तो C ++ में कचरा कलेक्टर क्यों नहीं था। मैं सोच रहा हूं कि कलेक्टर को इसमें क्यों नहीं जोड़ा जा सकता।


26
यह C ++ के बारे में शीर्ष दस मिथकों में से एक है जो नफरत करने वाले हमेशा उठाते हैं। कचरा संग्रह "इन" नहीं बनाया गया है, लेकिन इसे C ++ करने के कई आसान तरीके हैं। टिप्पणी पोस्ट करना क्योंकि दूसरों ने पहले से ही बेहतर जवाब दिया है नीचे मैं :)
davr

5
लेकिन यह बिल्ट-इन नहीं होने के बारे में पूरी बात है, आपको इसे स्वयं करना होगा। उच्च से निम्न की वास्तविकता: अंतर्निहित, पुस्तकालय, घर-निर्मित। मैं स्वयं सी ++ का उपयोग करता हूं, और निश्चित रूप से नफरत नहीं करता क्योंकि यह दुनिया की सबसे अच्छी भाषा है। लेकिन गतिशील स्मृति प्रबंधन एक दर्द है।
QBziZ

4
@ डावर - मैं सी ++ नफरत करने वाला नहीं हूं और न ही मैं यह तर्क देने की कोशिश भी नहीं कर रहा हूं कि सी ++ को एक कचरा उठाने वाले की जरूरत है। मैं पूछ रहा हूं क्योंकि मुझे पता है कि बज़्ने स्ट्रॉस्ट्रुप ने कहा है कि इसे जोड़ा जाएगा और बस उत्सुक थे कि इसे लागू नहीं करने के क्या कारण थे।
जेसन बेकर

1
यह आलेख डॉ। डॉब्स से सी और सी ++ के लिए बोहेम कलेक्टर एक ओपन सोर्स कचरा कलेक्टर का वर्णन करता है जिसका उपयोग सी और सी ++ दोनों के साथ किया जा सकता है। यह कुछ मुद्दों पर चर्चा करता है जो C ++ डिस्ट्रक्टर्स के साथ-साथ C स्टैंडर्ड लाइब्रेरी के साथ कचरा कलेक्टर का उपयोग करके उत्पन्न होते हैं।
रिचर्ड चैंबर्स

1
@rogerdpack: लेकिन यह अब तक उपयोगी नहीं है (मेरा उत्तर देखें ...) तो यह संभावना नहीं है कि कार्यान्वयन एक होने में निवेश करेगा।
einpoklum

जवाबों:


160

अंतर्निहित कचरा संग्रह में जोड़ा जा सकता था, लेकिन यह सिर्फ कटौती नहीं करता था। संभवतः न केवल कार्यान्वयन संबंधी जटिलताओं के कारण, बल्कि लोगों के कारण भी आम सहमति नहीं बन पाती है, जो कि काफी तेजी से होती हैं।

खुद बज्ने स्ट्रॉस्ट्रुप का एक उद्धरण:

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

यहाँ विषय की अच्छी चर्चा है

सामान्य अवलोकन:

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

C ++ को उन प्रतियोगियों के साथ बनाया गया था जिनके पास कचरा संग्रह नहीं था। दक्षता मुख्य चिंता थी कि C ++ को C और अन्य की तुलना में आलोचना को रोकना पड़ा।

कचरा संग्रहण के 2 प्रकार हैं ...

स्पष्ट कचरा संग्रह:

C ++ 0x में शेअर_प्ट्र के साथ बनाए गए पॉइंटर्स के माध्यम से कचरा संग्रह होगा

यदि आप चाहते हैं कि आप इसका उपयोग कर सकते हैं, यदि आप नहीं चाहते हैं कि आप इसका उपयोग करने के लिए मजबूर नहीं हैं।

यदि आप C ++ 0x के लिए प्रतीक्षा नहीं करना चाहते हैं, तो आप वर्तमान में बढ़ावा का उपयोग कर सकते हैं: साझा किया गया।

अवैध कचरा संग्रहण:

हालांकि इसमें पारदर्शी कचरा संग्रह नहीं है। हालांकि यह भविष्य के C ++ स्पेक्स के लिए फोकस बिंदु होगा।

Tr1 में निहित कचरा संग्रह क्यों नहीं है?

बहुत सारी चीज़ें हैं जो C ++ 0x का tr1 होनी चाहिए थीं, पिछले साक्षात्कारों में Bjarne Stroustrup ने कहा था कि tr1 के पास उतना नहीं था जितना उन्हें पसंद होगा।


71
अगर C ++ ने मुझ पर कचरा संग्रह के लिए मजबूर किया तो मैं एक नफरत बन जाऊंगा! लोग उपयोग क्यों नहीं कर सकते smart_ptr's? रास्ते में कूड़ा उठाने वाले के साथ आप निम्न स्तर की यूनिक्स शैली कैसे करेंगे? अन्य चीजें प्रभावित होंगी जैसे थ्रेडिंग। पायथन के पास अपना वैश्विक दुभाषिया ताला है, क्योंकि यह कचरा संग्रह है (साइथन देखें)। इसे C / C ++ से बाहर रखें, धन्यवाद।
unixman83

26
@ unixman83: संदर्भ के साथ मुख्य समस्या गिना हुआ कचरा संग्रह (यानी std::shared_ptr) चक्रीय संदर्भ है, जो स्मृति रिसाव का कारण बनता है। इसलिए आपको सावधानी से std::weak_ptrसाइकिल को तोड़ने के लिए उपयोग करना चाहिए , जो गड़बड़ है। मार्क और स्वीप स्टाइल GC को यह समस्या नहीं है। थ्रेडिंग / फोर्किंग और कचरा संग्रह के बीच कोई अंतर्निहित असंगति नहीं है। जावा और C # दोनों में उच्च प्रदर्शन प्रीमेप्टिव मल्टीथ्रेडिंग और एक कचरा कलेक्टर है। रीयलटाइम अनुप्रयोगों और एक कचरा कलेक्टर के साथ करने के लिए मुद्दे हैं, क्योंकि अधिकांश कचरा कलेक्टरों को चलाने के लिए दुनिया को रोकना पड़ता है।
एंड्रयू टोमाज़ोस

9
"संदर्भ के साथ मुख्य समस्या गिना हुआ कचरा संग्रह (यानी std::shared_ptr) चक्रीय संदर्भ है" और विस्मयकारी प्रदर्शन जो विडंबनापूर्ण है क्योंकि बेहतर प्रदर्शन आमतौर पर सी ++ का उपयोग करने का औचित्य है ... फ्लाइंगफ्रॉगब्लॉग.ब्लॉगस्पॉट.कॉम
जॉन हरोप

11
"आप निम्न स्तर की यूनिक्स शैली का उपयोग कैसे करेंगे"। ठीक उसी तरह जीसीएलई भाषाएं जैसे ओमेक्एल ~ 20 साल या उससे अधिक समय से कर रही हैं।
जॉन हैरोप

9
"पायथन के पास अपना वैश्विक दुभाषिया ताला है, क्योंकि यह कचरा संग्रह है।" स्ट्रॉमन तर्क। जावा और .NET दोनों में GCs हैं लेकिन दोनों में वैश्विक ताले नहीं हैं।
जॉन हैरोप

149

यहां बहस में जोड़ने के लिए।

कचरा संग्रह के साथ ज्ञात समस्याएं हैं, और उन्हें समझने से यह समझने में मदद मिलती है कि C ++ में कोई भी क्यों नहीं है।

1. प्रदर्शन?

पहली शिकायत अक्सर प्रदर्शन के बारे में होती है, लेकिन ज्यादातर लोग वास्तव में महसूस नहीं करते हैं कि वे किस बारे में बात कर रहे हैं। जैसा Martin Beckettकि समस्या से स्पष्ट है कि प्रति प्रदर्शन प्रदर्शन नहीं हो सकता है, लेकिन प्रदर्शन की भविष्यवाणी।

वर्तमान में जीसी के 2 परिवार हैं जो व्यापक रूप से तैनात हैं:

  • मार्क-एंड-स्वीप तरह
  • संदर्भ-गणना प्रकार

यह Mark And Sweepतेज़ है (समग्र प्रदर्शन पर कम प्रभाव) लेकिन यह "फ्रीज द वर्ल्ड" सिंड्रोम से ग्रस्त है: यानी जब जीसी अंदर आता है, तब तक बाकी सब कुछ बंद हो जाता है जब तक कि जीसी ने अपना क्लीनअप नहीं कर लिया। यदि आप कुछ मिलीसेकंड में जवाब देने वाले सर्वर का निर्माण करना चाहते हैं ... कुछ लेनदेन आपकी अपेक्षाओं पर खरे नहीं उतरेंगे :)

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

मैंने एफिल के कार्यान्वयनकर्ताओं द्वारा एक पेपर देखा है जो एक Reference Countingगारबेज कलेक्टर को लागू करने की कोशिश कर रहा था Mark And Sweepजो "फ्रीज द वर्ल्ड" पहलू के बिना एक समान वैश्विक प्रदर्शन होगा । यह GC (विशिष्ट) के लिए एक अलग थ्रेड की आवश्यकता है। एल्गोरिथ्म थोड़ा भयावह था (अंत में) लेकिन कागज ने एक समय में अवधारणाओं को पेश करने और एल्गोरिदम के विकास को "सरल" संस्करण से पूर्ण-विकसित तक दिखाने का एक अच्छा काम किया। अनुशंसित पढ़ने अगर केवल मैं अपने हाथों को पीडीएफ फाइल पर वापस रख सकता हूं ...

2. संसाधन अधिग्रहण प्रारंभिक है (RAII)

यह एक सामान्य मुहावरा है C++कि आप संसाधनों के स्वामित्व को एक वस्तु के भीतर लपेटेंगे ताकि यह सुनिश्चित हो सके कि वे ठीक से जारी हैं। इसका उपयोग ज्यादातर मेमोरी के लिए किया जाता है क्योंकि हमारे पास कचरा संग्रह नहीं है, लेकिन फिर भी यह कई अन्य स्थितियों के लिए भी उपयोगी है:

  • ताले (बहु धागा, फ़ाइल संभाल, ...)
  • कनेक्शन (एक डेटाबेस में, एक अन्य सर्वर, ...)

विचार वस्तु के जीवनकाल को ठीक से नियंत्रित करने का है:

  • इसे तब तक जीवित रहना चाहिए जब तक आपको इसकी आवश्यकता है
  • इसे मार दिया जाना चाहिए जब आप इसके साथ कर रहे हैं

जीसी की समस्या यह है कि अगर यह पूर्व में मदद करता है और अंततः गारंटी देता है कि बाद में ... यह "अंतिम" पर्याप्त नहीं हो सकता है। यदि आप लॉक जारी करते हैं, तो आप वास्तव में चाहेंगे कि इसे अभी जारी किया जाए, ताकि यह आगे की कॉल को ब्लॉक न करे!

जीसी के साथ भाषाओं में दो काम हैं:

  • जब स्टैक आवंटन पर्याप्त होता है तो GC का उपयोग न करें: यह सामान्य रूप से प्रदर्शन के मुद्दों के लिए है, लेकिन हमारे मामले में यह वास्तव में मदद करता है क्योंकि गुंजाइश जीवनकाल को परिभाषित करती है
  • usingनिर्माण ... लेकिन यह स्पष्ट (कमजोर) RAII है जबकि C ++ RAII में निहित है ताकि उपयोगकर्ता अनजाने में त्रुटि कर सकता है ( usingकीवर्ड को छोड़ कर )

3. स्मार्ट पॉइंटर्स

स्मार्ट पॉइंटर्स अक्सर मेमोरी को संभालने के लिए सिल्वर बुलेट के रूप में दिखाई देते हैं C++। अक्सर मैंने सुना है: हमें जीसी की आवश्यकता नहीं है, क्योंकि हमारे पास स्मार्ट पॉइंटर्स हैं।

एक और गलत नहीं हो सकता है।

स्मार्ट संकेत मदद करते हैं: auto_ptrऔर unique_ptrRAII अवधारणाओं का उपयोग करें, वास्तव में बेहद उपयोगी। वे इतने सरल हैं कि आप उन्हें अपने द्वारा काफी आसानी से लिख सकते हैं।

जब किसी को स्वामित्व साझा करने की आवश्यकता होती है, तो यह अधिक कठिन हो जाता है: आप कई थ्रेड्स के बीच साझा कर सकते हैं और गिनती को संभालने के साथ कुछ सूक्ष्म मुद्दे हैं। इसलिए, एक स्वाभाविक रूप से ओर जाता है shared_ptr

यह बहुत अच्छा है, यही सब के बाद बूस्ट है, लेकिन यह एक चांदी की गोली नहीं है। वास्तव में, इसके साथ मुख्य मुद्दा shared_ptrयह है कि यह एक जीसी द्वारा कार्यान्वित अनुकरण करता है Reference Countingलेकिन आपको अपने आप से साइकिल का पता लगाने की आवश्यकता है ...

बेशक यह weak_ptrबात सही है, लेकिन मैंने दुर्भाग्यवश shared_ptrउन चक्रों के कारण मेमोरी लीक को पहले ही देख लिया है ... और जब आप मल्टी थ्रेडेड वातावरण में होते हैं, तो इसका पता लगाना बहुत मुश्किल होता है!

4. समाधान क्या है?

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

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

तो वास्तव में, जीसी होना बहुत अच्छा होगा ... हालांकि यह कोई तुच्छ मुद्दा नहीं है। और इस समय के दौरान, हमें केवल अपनी आस्तीन को रोल करने की आवश्यकता है।


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

14
केवल दो प्रकार? संग्राहकों की नकल कैसे 'डटकर करें? पीढ़ीगत कलेक्टर? मिश्रित समवर्ती कलेक्टर (बेकर के कठिन वास्तविक समय ट्रेडमिल सहित)? विभिन्न संकर कलेक्टरों? यार, इस क्षेत्र के उद्योग में सरासर अज्ञानता मुझे कभी-कभी चकित कर देती है।
MY पर मेरा सही जनून

12
क्या मैंने कहा कि केवल 2 प्रकार थे? मैंने कहा कि 2 थे जो व्यापक रूप से तैनात थे। जहां तक ​​मुझे पता है कि पायथन, जावा और सी # सभी अब मार्क और स्वीप एल्गोरिदम का उपयोग करते हैं (जावा में एक संदर्भ कैमरा एल्गोरिदम होता था)। और भी अधिक सटीक होने के लिए, यह मुझे लगता है कि सी # की तुलना में मामूली चक्रों के लिए पीढ़ीगत जीसी का उपयोग करता है, प्रमुख चक्रों के लिए मार्क और स्वीप और स्मृति विखंडन से लड़ने के लिए नकल करना; हालाँकि मैं तर्क दूंगा कि एल्गोरिथम का दिल मार्क एंड स्वीप है। क्या आप किसी भी मुख्यधारा की भाषा जानते हैं जो दूसरी तकनीक का उपयोग करती है? मैं हमेशा सीखकर खुश हूं।
मैथ्यू एम।

3
आपने सिर्फ एक मुख्यधारा की भाषा का नाम दिया है जिसमें तीन का इस्तेमाल किया गया है।
JUST MY सही ओपिनियन

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

56

किस प्रकार का? क्या यह एम्बेडेड वॉशिंग मशीन कंट्रोलर, सेल फोन, वर्कस्टेशन या सुपर कंप्यूटर के लिए अनुकूलित होना चाहिए?
यह gui जवाबदेही या सर्वर लोडिंग को प्राथमिकता देना चाहिए?
यह बहुत सारी मेमोरी या बहुत सारे CPU का उपयोग करना चाहिए?

C / c ++ का उपयोग बहुत अधिक भिन्न परिस्थितियों में किया जाता है। मुझे संदेह है कि बढ़ावा देने वाले स्मार्ट पॉइंटर्स जैसे अधिकांश उपयोगकर्ता के लिए पर्याप्त होंगे

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


6
मैं निश्चित रूप से आपकी बात देख रहा हूं, लेकिन मैं यह पूछने के लिए मजबूर हूं: क्या जावा का उपयोग केवल कई अनुप्रयोगों के बारे में नहीं किया गया है?
जेसन बेकर

35
नहीं जावा उच्च प्रदर्शन अनुप्रयोगों के लिए उपयुक्त नहीं है, साधारण कारण यह है कि इसमें सी + + के समान ही प्रदर्शन की गारंटी नहीं है। तो आप इसे एक सेल फोन में पाएंगे, लेकिन आप इसे सेल स्विच या सुपर कंप्यूटर में नहीं पाएंगे।
जथ्रुस 29:08

11
आप हमेशा अधिक सर्वर खरीद सकते हैं, लेकिन आप हमेशा ग्राहक की जेब में सेल फ़ोन के लिए अधिक सीपीयू नहीं खरीद सकते हैं!
5

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

2
@ ज़तरस जावा ऑप्टिमाइज़िंग जीट के थ्रूपुट बी / सी पर जीत सकता है, हालांकि लेटेंसी (बू रियल-टाइम) नहीं, और निश्चित रूप से स्मृति पदचिह्न नहीं।
21

34

C ++ में कचरा संग्रह नहीं होने के सबसे बड़े कारणों में से एक यह है कि विनाशक के साथ अच्छा खेलने के लिए कचरा संग्रह प्राप्त करना वास्तव में बहुत कठिन है। जहां तक ​​मुझे पता है, कोई भी वास्तव में इसे पूरी तरह से हल करना नहीं जानता है। इससे निपटने के लिए बहुत सारे मुद्दे हैं:

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

ये केवल कुछ समस्याओं का सामना कर रहे हैं।


17
जीसी और डिस्ट्रक्टर्स ब्रेज़न से एक अच्छा कदम है। विध्वंसक GC के दौरान नहीं चलते हैं, क्योंकि यह GC की बात नहीं है। C ++ में GC अनंत स्मृति की धारणा बनाने के लिए मौजूद है , न कि अनंत अन्य संसाधनों के लिए।
11

2
यदि विध्वंसक नहीं चलते हैं तो भाषा के शब्दार्थ को पूरी तरह बदल देता है। मुझे लगता है कि बहुत कम से कम आपको एक नए कीवर्ड "gcnew" या कुछ और की आवश्यकता होगी ताकि आप स्पष्ट रूप से इस ऑब्जेक्ट को GC'ed होने दें (और इसलिए आपको इसका उपयोग स्मृति के अलावा संसाधनों को लपेटने के लिए नहीं करना चाहिए)।
ग्रेग रोजर्स

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

2
@ NateC-K: एक चीज जो जीसी बनाम गैर-जीसी (शायद सबसे बड़ी चीज) में सुधार है, यह गारंटी देने के लिए एक ठोस जीसी प्रणाली की क्षमता है कि हर संदर्भ उसी वस्तु को इंगित करता रहेगा जब तक संदर्भ मौजूद है। Disposeकिसी ऑब्जेक्ट पर कॉल करने से यह अस्थिर हो सकता है, लेकिन जब यह जीवित था तब ऑब्जेक्ट को इंगित किया गया संदर्भ मृत होने के बाद भी ऐसा करना जारी रखेगा। इसके विपरीत, गैर-जीसी प्रणालियों में, वस्तुओं को संदर्भों के मौजूद होने पर हटाया जा सकता है, और शायद ही कभी कहर की कोई सीमा होती है जो कि उन संदर्भों में से एक का उपयोग होने पर बर्बाद हो सकती है।
सुपरकैट

22

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

विशेष रूप से, C ++ मानक बाहरी रूप से अवलोकनीय व्यवहार के संदर्भ में भाषा को निर्दिष्ट करने के लिए काफी सावधान है, बजाय इसके कि कार्यान्वयन उस व्यवहार को कैसे प्राप्त करता है। कचरा संग्रहण के मामले में, तथापि, वहाँ है वास्तव में कोई बाहर से नमूदार व्यवहार।

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

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

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

फिर भी, यह C ++ के लिए प्रस्तावित किए गए प्रस्ताव से अधिक मजबूत है। पिछले प्रस्ताव [चेतावनी: पीडीएफ] (कि गिरा दिया गया) सब पर गारंटी कुछ भी नहीं किया। प्रस्ताव के 28 पृष्ठों में, बाहरी रूप से अवलोकनीय व्यवहार के रूप में आपको जो मिला वह एक एकल (गैर-मानक) नोट था:

[नोट: कचरा एकत्र किए गए कार्यक्रमों के लिए, एक उच्च गुणवत्ता वाले होस्ट किए गए कार्यान्वयन को अप्राप्य मेमोरी की मात्रा को अधिकतम करने का प्रयास करना चाहिए, जो इसे याद करता है। ध्यान दें]

कम से कम मेरे लिए, यह निवेश पर वापसी के बारे में एक गंभीर सवाल उठाता है । हम मौजूदा कोड को तोड़ने जा रहे हैं (किसी को यकीन नहीं है कि कितना, लेकिन निश्चित रूप से काफी थोड़ा), कार्यान्वयन पर नई आवश्यकताओं और कोड पर नए प्रतिबंध लगाते हैं, और बदले में हमें जो मिलता है वह संभवतः कुछ भी नहीं है?

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

मैं स्थिति को गणितीय रूप से संक्षेपित करूँगा: यह एक जटिल स्थिति है। जैसा कि कोई भी गणितज्ञ जानता है, एक जटिल संख्या के दो भाग होते हैं: वास्तविक और काल्पनिक। यह मुझे प्रतीत होता है कि हमारे यहां जो भी लागतें हैं वे वास्तविक हैं, लेकिन वे लाभ हैं जो (कम से कम अधिकतर) काल्पनिक हैं।


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

2
जावा में भी, GC वास्तव में उपयोगी AFAIK करने के लिए निर्दिष्ट नहीं है। यह freeआपके लिए कॉल कर सकता है (जहां मेरा मतलब freeसी भाषा के लिए विनम्र है)। लेकिन जावा कभी भी फाइनल या ऐसा कुछ भी कहने की गारंटी नहीं देता है। वास्तव में, C ++ जावा के अलावा कमिटेड डेटाबेस राइट्स, फ्लशिंग फाइल हैंडल्स इत्यादि को चलाने के लिए बहुत कुछ करता है। जावा "जीसी" का दावा करता है, लेकिन जावा डेवलपर्स को close()हर समय सावधानीपूर्वक कॉल करना पड़ता है और उन्हें संसाधन प्रबंधन के बारे में बहुत जागरूक होना पड़ता है, close()बहुत जल्द या बहुत देर से कॉल न करने के लिए सावधान रहना चाहिए । C ++ हमें उससे मुक्त करता है। ... (जारी)
हारून मैकडैड

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

@AaronMcDaid यह सच है कि GC गैर-मेमोरी संसाधनों के साथ बिल्कुल भी मदद नहीं करता है। सौभाग्य से, स्मृति की तुलना में ऐसे संसाधन बहुत कम आवंटित किए जाते हैं। इसके अलावा, उनमें से 90% से अधिक को उस विधि से मुक्त किया जा सकता है जिसने उन्हें आवंटित किया था, इसलिए try (Whatever w=...) {...}इसे हल करता है (और जब आप भूल जाते हैं तो आपको चेतावनी मिलती है)। शेष भी RAII के साथ समस्याग्रस्त हैं। कॉलिंग close()"सभी समय" का अर्थ हो सकता है एक बार हजार लाइनों के दसियों प्रति है, ताकि है कि बुरा नहीं है, जबकि स्मृति हर जावा लाइन पर लगभग आवंटित हो जाता है।
Maaartinus

15

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

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

स्रोत: http://www.stroustrup.com/bs_faq.html#garbage-collection

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

उम्मीद है की यह मदद करेगा।


"एक प्रदर्शन के साथ जो अन्य कचरा एकत्र भाषाओं के साथ अनुकूल रूप से तुलना करता है"। प्रशस्ति पत्र?
जॉन हैरोप

1
मेरा लिंक टूट गया था। मैंने यह जवाब 5 साल पहले लिखा था।
रेने

1
ठीक है, मैं इन दावों के कुछ स्वतंत्र सत्यापन की उम्मीद कर रहा था, अर्थात स्ट्रॉस्ट्रुप या बोहम द्वारा नहीं। :-)
जॉन हैरोप

12

स्ट्रॉस्ट्रुप ने 2013 गोइंग नेटिव सम्मेलन में इस पर कुछ अच्छी टिप्पणियां कीं।

बस इस वीडियो में लगभग 25m50s पर जाएं । (मैं वास्तव में पूरे वीडियो को देखने की सलाह दूंगा, लेकिन यह कचरा संग्रहण के बारे में पूरी जानकारी देता है।)

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

आधुनिक C ++, और हमारे पास C ++ 11 में सामान के साथ, कचरा संग्रह अब सीमित परिस्थितियों में छोड़कर वांछनीय नहीं है। वास्तव में, भले ही एक अच्छा कचरा संग्रहकर्ता प्रमुख C ++ संकलक में से एक में बनाया गया हो, मुझे लगता है कि इसका उपयोग बहुत बार नहीं किया जाएगा। जीसी से बचना आसान होगा , कठिन नहीं।

वह इस उदाहरण को दर्शाता है:

void f(int n, int x) {
    Gadget *p = new Gadget{n};
    if(x<100) throw SomeException{};
    if(x<200) return;
    delete p;
}

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

तो उपाय क्या है? कुछ दृष्टिकोण हैं। स्पष्ट दृष्टिकोण, जिसे आप अपनी अधिकांश वस्तुओं के लिए उपयोग करेंगे:

void f(int n, int x) {
    Gadget p = {n};  // Just leave it on the stack (where it belongs!)
    if(x<100) throw SomeException{};
    if(x<200) return;
}

यह टाइप करने के लिए कम वर्ण लेता है। यह newरास्ते में नहीं हो रहा है। यह आपको Gadgetदो बार टाइप करने की आवश्यकता नहीं है । फ़ंक्शन के अंत में ऑब्जेक्ट को नष्ट कर दिया जाता है। यदि आप यही चाहते हैं, तो यह बहुत सहज है। Gadgetके रूप में ही व्यवहार करते हैं intया double। भविष्य कहनेवाला, आसानी से पढ़ा जाने वाला, आसानी से पढ़ाने वाला। सब कुछ एक 'मूल्य' है। कभी-कभी एक बड़ा मूल्य, लेकिन मूल्यों को सिखाना आसान होता है क्योंकि आपके पास यह 'कार्रवाई दूरी पर' चीज नहीं है जो आपको संकेत (या संदर्भ) के साथ मिलती है।

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

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

यह एक दक्षता समस्या की तरह लग सकता है। क्या होगा अगर मैं एक गैजेट को वापस करना चाहता हूं foo()? C ++ 11 के चाल शब्दार्थ बड़ी वस्तुओं को वापस करना आसान बनाते हैं। बस लिखें Gadget foo() { ... }और यह सिर्फ काम करेगा, और जल्दी से काम करेगा। आपको &&खुद के साथ गड़बड़ करने की ज़रूरत नहीं है , बस चीजों को मूल्य से वापस करें और भाषा अक्सर आवश्यक अनुकूलन कर पाएगी। (C ++ 03 से पहले भी, कंपाइलरों ने अनावश्यक नकल से बचने के लिए उल्लेखनीय रूप से अच्छा काम किया।)

जैसा कि स्ट्रॉस्ट्रप ने वीडियो में कहीं और कहा है (विरोधाभासी): "केवल एक कंप्यूटर वैज्ञानिक एक वस्तु की नकल करने पर जोर देगा, और फिर मूल को नष्ट कर देगा। (दर्शक हँसते हैं)। बस वस्तु को सीधे नए स्थान पर क्यों नहीं ले जाना है? यह मनुष्य है (कंप्यूटर वैज्ञानिक नहीं) उम्मीद करते हैं। "

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

यदि यह आप के लिए काम नहीं करता है, तो आप उपयोग कर सकते हैं unique_ptr, या उस में नाकाम रहने, shared_ptr। अच्छी तरह से लिखा C ++ 11 कम है, पढ़ने में आसान है, और स्मृति प्रबंधन की बात आती है तो कई अन्य भाषाओं की तुलना में आसान है।


1
GC का उपयोग केवल उन वस्तुओं के लिए किया जाना चाहिए जो संसाधनों का अधिग्रहण नहीं करते हैं (अर्थात अन्य संस्थाओं को अपनी ओर से "अगली सूचना तक" करने के लिए कहें)। यदि Gadgetअपनी ओर से कुछ और करने के लिए नहीं कहता है, तो मूल कोड जावा में पूरी तरह से सुरक्षित होगा यदि अर्थहीन (जावा) deleteकथन को हटा दिया गया था।
सुपरकैट

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

जीसी और आरएआई-प्रबंधित ढेर वस्तुओं के लिए एक अच्छी प्रकार की प्रणाली के विभिन्न प्रकार होने चाहिए, क्योंकि कुछ उपयोग पैटर्न एक के साथ बहुत अच्छी तरह से काम करते हैं और दूसरे के साथ बहुत खराब होते हैं। .NET या Java में, एक स्टेटमेंट string1=string2;स्ट्रिंग की लंबाई की परवाह किए बिना बहुत तेज़ी से निष्पादित करेगा (यह सचमुच एक रजिस्टर लोड और रजिस्टर स्टोर से अधिक कुछ नहीं है), और यह सुनिश्चित करने के लिए किसी लॉकिंग की आवश्यकता नहीं है कि यदि उपरोक्त कथन निष्पादित किया गया string2है तो लिखा जा रहा है, string1कोई अपरिभाषित व्यवहार के साथ या तो पुराने मूल्य या नए मूल्य को धारण करेगा)।
सुपरकैट

C ++ में, असाइनमेंट के shared_ptr<String>पीछे बहुत-से-सीन-सिंक्रोनाइज़ेशन की आवश्यकता होती है, और Stringएक वेरिएबल को एक साथ पढ़ने और लिखने पर अजीब तरीके से असाइन किया जा सकता है। ऐसे मामले जहां कोई एक Stringसाथ लिखना और पढ़ना चाहेगा , वह बहुत सामान्य नहीं है, लेकिन अगर कुछ कोड अन्य थ्रेड्स के लिए चल रहे स्टेटस रिपोर्ट उपलब्ध कराना चाहते हैं, तो यह उत्पन्न हो सकता है। .NET और जावा में, ऐसी चीजें सिर्फ "काम" करती हैं।
सुपरकैट

1
@curiousguy कुछ भी नहीं बदला है, जब तक कि आप सही सावधानी नहीं बरतते हैं, तब भी जावा अंतिम रूप से निर्माणकर्ता के समाप्त होते ही कॉल करने की अनुमति देता है। यहाँ एक वास्तविक जीवन उदाहरण: " अंतिम रूप () जावा 8 में दृढ़ता से पहुंच योग्य वस्तुओं पर बुलाया गया है "। निष्कर्ष यह है कि इस सुविधा का उपयोग कभी न करें, कि लगभग हर कोई भाषा की एक ऐतिहासिक डिजाइन गलती मानता है। जब हम उस सलाह का पालन करते हैं, तो भाषा उस नियतत्व को प्रदान करती है जिससे हम प्यार करते हैं।
होल्गर

11

क्योंकि आधुनिक C ++ को कचरा संग्रहण की आवश्यकता नहीं है।

इस मामले पर ब्रेज़न स्ट्रॉस्ट्रप का अक्सर पूछे जाने वाले प्रश्न का उत्तर है :

मुझे कचरा पसंद नहीं है। मैं कूड़ा डालना पसंद नहीं करता। मेरा आदर्श किसी भी कचरे का उत्पादन न करके कचरे के संग्रहकर्ता की आवश्यकता को समाप्त करना है। यह अब संभव है।


इन दिनों लिखे गए कोड के लिए स्थिति (C ++ 17 और आधिकारिक कोर दिशानिर्देशों का पालन ) निम्नानुसार है:

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

"अरे हाँ, लेकिन क्या ...

... अगर मैं कोड लिखता हूं जिस तरह से हम पुराने दिनों में C ++ लिखते थे? "

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

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

... अगर मैं reintrepret_cast? या जटिल सूचक अंकगणित करते हैं? या ऐसे अन्य हैक?

वास्तव में, यदि आप अपना दिमाग लगाते हैं, तो आप कोड लिख सकते हैं जो दिशानिर्देशों के साथ अच्छा खेलने के बावजूद चीजों को गड़बड़ कर देता है। परंतु:

  1. आप शायद ही कभी ऐसा करेंगे (कोड के स्थानों के संदर्भ में, निष्पादन समय के अंश के संदर्भ में आवश्यक नहीं)
  2. आप केवल जानबूझकर ऐसा करेंगे, संयोगवश नहीं।
  3. ऐसा करने से दिशानिर्देशों के अनुरूप एक कोडबेस में खड़ा हो जाएगा।
  4. यह उस तरह का कोड है जिसमें आप जीसी को किसी अन्य भाषा में वैसे भी बाईपास करेंगे।

... पुस्तकालय विकास? "

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


तो, यह ऐसा ही है जैसे कि बज़्ने ने कहा: आम तौर पर कचरा इकट्ठा करने के लिए वास्तव में कोई प्रेरणा नहीं है, जैसा कि आप सभी करते हैं, लेकिन सुनिश्चित करें कि कचरा पैदा न करें। C ++ के साथ GC एक गैर-समस्या बन रहा है।

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


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

2
@ user1863152: यह एक ऐसा मामला है जिसमें एक कस्टम आवंटनकर्ता उपयोगी होगा। यह अभी भी एक भाषा अभिन्न जीसी की आवश्यकता नहीं है ...
einpoklum

को ईनोपोकलम: सच। यह केवल पाठ्यक्रमों के लिए घोड़ा है। मेरी आवश्यकता परिवहन यात्री सूचना के गतिशील रूप से बदलने वाले गैलन को संसाधित करने की थी। आकर्षक विषय .. वास्तव में सॉफ्टवेयर दर्शन के लिए नीचे आता है।
www-0av-Com

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

10

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

कुछ भी आपको स्मार्ट पॉइंटर्स का उपयोग करने से रोकता है जो कुछ तृतीय-पक्ष कचरा संग्रह तंत्र से बंधे हैं। मुझे लगता है कि Microsoft को COM के साथ कुछ ऐसा करना याद है और यह अच्छी तरह से नहीं हुआ।


2
मुझे नहीं लगता कि GC को VM की आवश्यकता है। कंपाइलर वैश्विक स्थिति को अपडेट करने के लिए सभी पॉइंटर ऑपरेशन में कोड जोड़ सकता है, जबकि पृष्ठभूमि में एक अलग धागा ऑब्जेक्ट्स को आवश्यकतानुसार हटाता है।
user83255

3
मैं सहमत हूँ। आपको एक वर्चुअल मशीन की आवश्यकता नहीं है, लेकिन दूसरी, जब आप अपने लिए अपनी स्मृति को प्रबंधित करना शुरू करते हैं, तो पृष्ठभूमि में, मेरा मानना ​​है कि आपने वास्तविक "बिजली के तारों" को छोड़ दिया है और एक वीएम स्थिति की तरह है।
उड़ी


4

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

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

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


यह वास्तव में एक अच्छा बिंदु है, मैंने अभी हाल ही में अपने संकेत से कुछ बिट चुराए हैं क्योंकि अन्यथा कैश की मात्रा में कमी होगी।
राहगीर

@PasserBy: मुझे आश्चर्य है कि 64-बिट पॉइंटर्स का उपयोग करने वाले कितने एप्लिकेशन ऑब्जेक्ट संदर्भों के रूप में स्केल किए गए 32-बिट पॉइंटर्स का उपयोग करने से अधिक लाभान्वित होंगे, या फिर पता स्थान के 4GiB में लगभग सब कुछ रखते हुए और उच्च से डेटा को पुनः प्राप्त / संग्रहीत करने के लिए विशेष ऑब्जेक्ट का उपयोग करने के लिए। -सुविधा से परे भंडारण? मशीनों में इतनी रैम होती है कि 64-बिट पॉइंटर्स की रैम की खपत कोई मायने नहीं रखती है, सिवाय इसके कि वे 32-बिट पॉइंटर्स की तुलना में दो बार ज्यादा कैश का जुगाड़ करते हैं।
सुपरकैट

3

सभी तकनीकी बातचीत अवधारणा को पूरा कर रही है।

यदि आप सभी मेमोरी के लिए जीसी को सी ++ में स्वचालित रूप से डालते हैं तो वेब ब्राउज़र की तरह कुछ पर विचार करें। वेब ब्राउज़र को एक पूर्ण वेब दस्तावेज़ लोड करना चाहिए और वेब स्क्रिप्ट चलाना चाहिए। आप दस्तावेज़ ट्री में वेब स्क्रिप्ट चर स्टोर कर सकते हैं। एक ब्राउज़र में BIG डॉक्यूमेंट में बहुत सारे टैब खुले हैं, इसका मतलब है कि हर बार GC को एक पूर्ण संग्रह करना होगा और उसे सभी दस्तावेज़ तत्वों को स्कैन भी करना होगा।

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

तो सही समाधान है कि किसी एप्लिकेशन को उन भागों में विभाजित करें, जिन्हें GC की जरूरत है और जो हिस्से नहीं हैं। ऊपर दिए गए वेब ब्राउज़र उदाहरण के मामले में, यदि दस्तावेज़ ट्री को मॉलोक के साथ आवंटित किया गया था, लेकिन जावास्क्रिप्ट जीसी के साथ चला गया, तो हर बार जीसी किक में केवल मेमोरी का एक छोटा सा हिस्सा स्कैन किया जाता है और मेमोरी के सभी आउट किए गए तत्व दस्तावेज़ ट्री को वापस पृष्ठांकित करने की आवश्यकता नहीं है।

इस समस्या को और समझने के लिए, वर्चुअल मेमोरी पर नज़र डालें और इसे कंप्यूटरों में कैसे लागू किया जाए। यह सब इस तथ्य के बारे में है कि 2GB कार्यक्रम के लिए उपलब्ध है जब वास्तव में इतना रैम नहीं है। 32 जीबी सिस्टम के लिए 2 जीबी रैम वाले आधुनिक कंप्यूटरों में यह ऐसी समस्या नहीं है, बशर्ते केवल एक कार्यक्रम चल रहा हो।

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

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

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


3

SHORT ANSWER: हम यह नहीं जानते कि कचरा संग्रहण कुशलतापूर्वक (मामूली समय और स्थान के साथ) कैसे किया जाए और हर समय (सभी संभावित मामलों में) सही तरीके से किया जाए।

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

यह सवाल को विराम देता है: कचरा संग्रह प्रदर्शन में बाधा क्यों बनता है? मुख्य कारण यह है कि, जब इसे लागू करने की बात आती है, तो हम [कंप्यूटर वैज्ञानिक] सभी मामलों के लिए, कम से कम ओवरहेड के साथ कचरा संग्रह करना नहीं जानते हैं। इसलिए हर समय कचरा संग्रह करने के लिए C ++ कंपाइलर और रनटाइम सिस्टम के लिए असंभव है। दूसरी ओर, C ++ प्रोग्रामर को अपने डिजाइन / कार्यान्वयन को जानना चाहिए और वह यह तय करने के लिए सबसे अच्छा व्यक्ति है कि कचरा संग्रह कैसे करें।

अंतिम, यदि नियंत्रण (हार्डवेयर, विवरण, आदि) और प्रदर्शन (समय, स्थान, शक्ति, आदि) मुख्य बाधाएं नहीं हैं, तो C ++ लेखन उपकरण नहीं है। अन्य भाषा बेहतर सेवा दे सकती है और आवश्यक ओवरहेड के साथ अधिक [छिपी] रनटाइम प्रबंधन की पेशकश कर सकती है।


3

जब हम C ++ की जावा से तुलना करते हैं, तो हम देखते हैं कि C ++ को निहित कचरा संग्रह को ध्यान में रखकर नहीं बनाया गया था, जबकि जावा था।

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

इसके अलावा, C ++ एक ऐसी भाषा है जिसका उद्देश्य एक जटिल रन-टाइम वातावरण होने के बजाय स्टैंडअलोन निष्पादन योग्य के रूप में चलाना है।

सभी के सभी: हाँ, यह संभव हो सकता है कि C पर कचरा संग्रह जोड़ा जाए, लेकिन निरंतरता के लिए ऐसा न करना बेहतर है।


1
मुक्त स्मृति और चल रहे विध्वंसक भी पूरी तरह से अलग मुद्दे हैं। (जावा में विध्वंसक नहीं है, जो कि पीटीए है।) जीसी मेमोरी को मुक्त करता है, यह डावर नहीं चलाता है।
जिज्ञासु

0

मुख्य रूप से दो कारणों से:

  1. क्योंकि इसे एक (IMHO) की आवश्यकता नहीं है
  2. क्योंकि यह RAII के साथ बहुत अधिक असंगत है, जो C ++ की आधारशिला है

C ++ पहले से ही मैनुअल मेमोरी मैनेजमेंट, स्टैक एलोकेशन, RAII, कंटेनर्स, ऑटोमैटिक पॉइंटर्स, स्मार्ट पॉइंटर्स ... ऑफर करता है ... जो पर्याप्त होना चाहिए। कचरा संग्रहकर्ता आलसी प्रोग्रामर के लिए हैं, जो यह सोचने में 5 मिनट खर्च नहीं करना चाहते हैं कि किसके पास कौन सी वस्तुएं होनी चाहिए या कब संसाधनों को मुक्त करना चाहिए। ऐसा हम C ++ में नहीं करते हैं।


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

0

कचरा संग्रह को कम करना वास्तव में उच्च स्तर के प्रतिमान बदलाव का निम्न स्तर है।

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

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