कचरा संग्रहण के लिए एक अच्छा विचार कब है?


135

इसलिए मैं C # कचरा संग्राहक को चलाने के लिए एक सवाल पढ़ रहा था जहां लगभग हर एक जवाब एक ही है: आप इसे कर सकते हैं, लेकिन आपको कुछ दुर्लभ मामलों को छोड़कर नहीं करना चाहिए । अफसोस की बात है कि इस तरह के मामले क्या हैं, इस बारे में कोई नहीं बताता।

क्या आप मुझे बता सकते हैं कि कचरा संग्रहण के लिए वास्तव में किस तरह का परिदृश्य एक अच्छा या उचित विचार है?

मैं C # विशिष्ट मामलों के लिए नहीं कह रहा हूं, बल्कि उन सभी प्रोग्रामिंग भाषाओं में, जिनके पास कचरा कलेक्टर है। मुझे पता है कि आप सभी भाषाओं पर जावा की तरह जीसी को बाध्य नहीं कर सकते हैं, लेकिन मान लीजिए कि आप कर सकते हैं।


17
"लेकिन बल्कि, सभी प्रोग्रामिंग भाषाएँ जिनके पास कचरा कलेक्टर है" विभिन्न भाषाएं (या, अधिक ठीक से, अलग-अलग कार्यान्वयन ) कचरा संग्रह के लिए विभिन्न तरीकों का उपयोग करती हैं, इसलिए आपको एक आकार-फिट-सभी नियम खोजने की संभावना नहीं है।
कर्नल थर्टी टू

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

3
मैं इस धारणा के अधीन था कि यदि आप कठिन वास्तविक समय सीमा की उम्मीद कर रहे थे, तो आप पहली बार में जीसी भाषा का उपयोग नहीं करेंगे।
ग्रेग्रास

4
मैं यह नहीं देख सकता कि आप इसका उत्तर गैर-वीएम-विशिष्ट तरीके से कैसे दे सकते हैं। 32-बिट प्रक्रियाओं के लिए प्रासंगिक, 64-बिट प्रक्रियाओं के लिए प्रासंगिक। .NET JVM और उच्च अंत के लिए
rwong

3
@DavidConrad आप इसे C # में बाध्य कर सकते हैं। इसलिए सवाल।
ओमेगा

जवाबों:


127

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

आप किसी भी तर्क या कारण के साथ ऐसा करने के लिए जीसी के व्यवहार को बहुत अच्छी तरह से जानते होंगे।

संग्रह की एकमात्र सलाह जो मैं दे सकता हूं वह है: कभी मत करना।

यदि आप वास्तव में GC के जटिल विवरण जानते हैं, तो आपको मेरी सलाह की आवश्यकता नहीं होगी, इसलिए यह कोई फर्क नहीं पड़ेगा। यदि आप पहले से ही 100% आत्मविश्वास के साथ नहीं जानते हैं तो इससे ऑनलाइन मदद मिलेगी और इस तरह का उत्तर ढूंढना होगा: आपको GC.Clectlect या वैकल्पिक रूप से कॉल नहीं करना चाहिए : आपको यह जानना चाहिए कि GC कैसे काम करता है? अंदर और बाहर, और उसके बाद ही आपको उत्तर पता चलेगा

वहाँ एक सुरक्षित जगह यह कुछ अर्थों GC.Collect उपयोग करने के लिए बनाता है :

GC.Collect एक एपीआई उपलब्ध है जिसका उपयोग आप चीजों की रूपरेखा समय के लिए कर सकते हैं। आप एक एल्गोरिथ्म को प्रोफाइल कर सकते हैं, एकत्र कर सकते हैं, और दूसरे एल्गोरिथ्म को तुरंत बाद में जान सकते हैं, यह जानकर कि आपके दूसरे एक के परिणामों को तिरछा करने के दौरान पहले अहंकार की जीसी नहीं हो रही थी।

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


वैसे भी वंचित उदाहरण

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

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


तो चलिए .NET GC में कुछ शब्दार्थ और तंत्र के बारे में बात करते हैं ... या ..

सब कुछ मुझे लगता है कि मैं .NET जीसी के बारे में जानता हूं

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

नीचे जानबूझकर कई विवरण गायब हैं जिनके बारे में मैं निश्चित नहीं हूं, साथ ही साथ जानकारी का एक बड़ा निकाय जिससे मैं अनजान हूं। इस सूचना का उपयोग अपने ख़ुद के जोखिम पर करें।


जीसी अवधारणाओं

.NET GC असंगत समय पर होता है, यही कारण है कि इसे "गैर-नियतात्मक" कहा जाता है, इसका मतलब है कि आप विशिष्ट समय पर होने के लिए इस पर भरोसा नहीं कर सकते। यह एक जेनरेशनल कचरा संग्रहकर्ता भी है, जिसका अर्थ है कि यह आपकी वस्तुओं को कितने जीसी पास से गुजरता है जिसके माध्यम से वे गुजारे हैं।

जनरल 0 ढेर में ऑब्जेक्ट्स 0 संग्रह के माध्यम से रहते हैं, ये हाल ही में बनाए गए हैं इसलिए हाल ही में उनके इंस्टेंटेशन के बाद से कोई संग्रह नहीं हुआ है। आपके जनरल 1 ढेर में ऑब्जेक्ट एक संग्रह पास से गुजरे हैं, और इसी तरह आपके जनरल 2 ढेर में ऑब्जेक्ट 2 संग्रह पास से गुजरे हैं।

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

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


एक संग्रह में क्या है

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

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

अब स्मृति के 3 ढेर के विचार को देखते हुए, सभी संग्रह की संख्या से गुजरते हैं जो वे गुजरे हैं, आइए बात करते हैं कि ये बदलाव क्यों मौजूद हैं।


जनरल ० संग्रह

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


जनरल 1 संग्रह

जनरल 1 ऑब्जेक्ट्स, जो वस्तुओं के इस बहुत ही अस्थायी श्रेणी में नहीं आते हैं, अभी भी कम समय तक जीवित रह सकते हैं, क्योंकि अभी भी- निर्मित वस्तुओं का एक बड़ा हिस्सा लंबे समय तक उपयोग नहीं किया जाता है। इसलिए Gen 1 बार-बार इकट्ठा करता है, फिर से इसे छोटा रखता है इसलिए यह संग्रह तेज होता है। हालाँकि यह धारणा कम है कि यह वस्तुएँ Gen 0 से अस्थायी हैं, इसलिए यह Gen 0 की तुलना में कम बार एकत्रित होती हैं

मैं कहूंगा कि मैं स्पष्ट रूप से उन तकनीकी तंत्रों को नहीं जानता हूं जो जनरल 0 के संग्रह पास और जनरल 1 के बीच भिन्न हैं, यदि वे आवृत्ति को इकट्ठा करने के अलावा अन्य सभी पर हैं।


जनरल 2 संग्रह

जनरल 2 अब सभी की माँ को सही होना चाहिए? खैर, हां, यह कमोबेश सही है। यह वह जगह है जहां आपके सभी स्थायी ऑब्जेक्ट रहते हैं - Main()उदाहरण के लिए आपके जीवन की वस्तु , और सब कुछ जो Main()संदर्भ देता है क्योंकि वे Main()आपकी प्रक्रिया के अंत तक आपके रिटर्न तक निहित होंगे ।

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


बड़ी वस्तु ढेर

जनरल 2 के अतिरिक्त व्यवहार का एक उदाहरण यह है कि यह लार्ज ऑब्जेक्ट हीप पर संग्रह भी करता है। अब तक मैं पूरी तरह से स्मॉल ऑब्जेक्ट हीप के बारे में बात कर रहा हूं, लेकिन .NET रनटाइम ने कुछ आकारों की चीजों को एक अलग हीप के लिए आवंटित किया है क्योंकि मैंने ऊपर संकलन के रूप में संदर्भित किया था। जब छोटे ऑब्जेक्ट हीप पर संग्रह समाप्त होता है, तो कंप्रेशन को ऑब्जेक्ट्स को घूमने की आवश्यकता होती है। अगर Gen 1 में जीवित 10mb ऑब्जेक्ट है, तो संग्रह के बाद संघनन को पूरा करने में अधिक समय लगने वाला है, इस प्रकार Gen 1 का संग्रह धीमा हो जाता है। ताकि 10mb ऑब्जेक्ट बड़े ऑब्जेक्ट हीप को आबंटित किया जाए, और Gen 2 के दौरान एकत्र किया जाए जो इतनी बार चलता है।


अंतिम रूप

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

जिस तंत्र के साथ फाइनल करते हैं वह अंतिम रूप से सीधे कतार में संदर्भित होने से होता है। जब रनटाइम किसी ऑब्जेक्ट को अंतिम रूप से आवंटित करता है, तो यह उस ऑब्जेक्ट को अंतिम रूप देने के लिए एक संकेतक जोड़ता है, और आपकी ऑब्जेक्ट को जगह में लॉक करता है (जिसे पिनिंग कहा जाता है) इसलिए संघनन इसे स्थानांतरित नहीं करेगा जो अंतिमकरण कतार संदर्भ को तोड़ देगा। जैसे-जैसे संग्रह पास होते जाते हैं, अंततः आपकी वस्तु को जीसी रूट नहीं मिलेगा, लेकिन इसे एकत्र करने से पहले अंतिम रूप दिया जाना चाहिए। इसलिए जब ऑब्जेक्ट मृत हो जाता है, तो संग्रह इसे अंतिम पंक्ति से संदर्भ स्थानांतरित कर देगा और उस पर एक संदर्भ रख देगा जिसे "FReachable" कतार के रूप में जाना जाता है। फिर संग्रह जारी है। भविष्य में एक और "गैर-नियतात्मक" समय पर, फाइनल थ्रेड के रूप में जाना जाने वाला एक अलग धागा FReachable क्यू के माध्यम से जाएगा, संदर्भित ऑब्जेक्ट्स में से प्रत्येक के लिए फाइनलर्स को निष्पादित करेगा। यह समाप्त होने के बाद, FReachable कतार खाली है, और यह प्रत्येक ऑब्जेक्ट के हेडर पर थोड़ा सा फ़्लिप किया है जो कहता है कि उन्हें अंतिम रूप देने की आवश्यकता नहीं है (इस बिट को मैन्युअल रूप से भी फ़्लिप किया जा सकता हैGC.SuppressFinalizeजो Dispose()तरीकों में आम है ), मुझे यह भी संदेह है कि इसने वस्तुओं को अनपिन कर दिया है, लेकिन मुझे उस पर उद्धृत नहीं करते हैं। अगला संग्रह जो इस वस्तु के ढेर पर आता है, वह अंत में इसे एकत्र करेगा। जनरल 0 संग्रह भी उस अंतिमकरण की आवश्यकता वाली वस्तुओं पर ध्यान नहीं देते हैं, यह स्वचालित रूप से उन्हें बढ़ावा देता है, यहां तक ​​कि उनकी जड़ की जांच किए बिना। एक अनियंत्रित वस्तु को Gen 1 में अंतिम रूप देने की आवश्यकता है, FReachableकतार में उछाला जाएगा , लेकिन संग्रह इसके साथ कुछ और नहीं करता है, इसलिए यह Gen 2 में रहता है। इस तरह से, सभी ऑब्जेक्ट्स जिनमें एक फाइनल होता है, और नहीं GC.SuppressFinalizeजनरल 2 में एकत्र किया जाएगा।


4
@FlorianMargaine हाँ ... सभी कार्यान्वयनों में "जीसी" के बारे में कुछ भी कहने से वास्तव में कोई मतलब नहीं है ..
जिमी हॉफ

10
tl; dr: इसके बजाय ऑब्जेक्ट पूल का उपयोग करें।
रॉबर्ट हार्वे

5
tl; dr: टाइमिंग / प्रोफाइलिंग के लिए, यह उपयोगी हो सकता है।
कुत्सकेम

3
@ यांत्रिकी के ऊपर मेरे विवरण को पढ़ने के बाद (जैसा कि मैं उन्हें समझता हूं), जैसा कि आप इसे देखते हैं तो क्या लाभ होगा? आप बड़ी संख्या में वस्तुओं को साफ करते हैं - SOH (या LOH?) में। क्या आपने इस संग्रह के लिए अन्य थ्रेड्स को रोक दिया था? क्या यह संग्रह सिर्फ दो वस्तुओं को दो बार बढ़ावा देता है Gen 2 के रूप में इसे हटा दिया गया है? क्या LOH पर संकलन का कारण था (क्या आपने इसे चालू किया है?)। आपके पास कितने GC ढेर हैं और सर्वर या डेस्कटॉप मोड में आपका GC है? जीसी एक feckin 'बर्फ berg है, विश्वासघाती पानी के नीचे है। बस स्पष्ट है। मैं आराम से इकट्ठा करने के लिए पर्याप्त स्मार्ट नहीं हूं।
जिमी होफा

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

68

अफसोस की बात है कि इस तरह के मामले क्या हैं, इस बारे में कोई नहीं बताता।

मैं कुछ उदाहरण दूंगा। सभी में यह दुर्लभ है कि एक जीसी को मजबूर करना एक अच्छा विचार है लेकिन यह पूरी तरह से इसके लायक हो सकता है। यह उत्तर .NET और जीसी साहित्य के साथ मेरे अनुभव से है। इसे अन्य प्लेटफार्मों (कम से कम जिनके पास एक महत्वपूर्ण जीसी है) के लिए अच्छी तरह से सामान्यीकृत करना चाहिए।

  • विभिन्न प्रकार के बेंचमार्क । आप एक ज्ञात प्रबंधित हीप स्थिति चाहते हैं जब एक बेंचमार्क शुरू होता है ताकि जीसी बेंचमार्क के दौरान यादृच्छिक रूप से ट्रिगर न हो। जब आप एक बेंचमार्क दोहराते हैं तो आप एक ही संख्या और प्रत्येक पुनरावृत्ति में जीसी के काम की मात्रा चाहते हैं।
  • संसाधनों की अचानक रिहाई। उदाहरण के लिए एक महत्वपूर्ण GUI विंडो बंद करना या कैश को रीफ्रेश करना (और इस तरह पुरानी संभावित बड़ी कैश सामग्री जारी करना)। GC यह पता नहीं लगा सकता क्योंकि आप जो भी कर रहे हैं वह अशक्त करने के लिए एक संदर्भ सेट कर रहा है। तथ्य यह है कि यह एक पूरे ऑब्जेक्ट ग्राफ को अनाथ करता है आसानी से पता लगाने योग्य नहीं है।
  • की रिहाई अप्रबंधित संसाधन है कि लीक हो । यह निश्चित रूप से कभी नहीं होना चाहिए, लेकिन मैंने ऐसे मामलों को देखा है जहां एक 3 पार्टी लाइब्रेरी ने सामान लीक किया है (जैसे COM ऑब्जेक्ट)। डेवलपर को कभी-कभी एक संग्रह के लिए प्रेरित किया जाता था।
  • इंटरएक्टिव अनुप्रयोगों जैसे खेल । खेलने के दौरान खेलों में प्रति फ्रेम (60Hz => 16ms प्रति फ्रेम) बहुत सख्त बजट होता है। Hickups से बचने के लिए आपको GCs से निपटने के लिए एक रणनीति की आवश्यकता होती है। इस तरह की एक रणनीति जी 2 जीसी को अधिक से अधिक विलंबित करना है और उन्हें एक उपयुक्त समय जैसे कि लोडिंग स्क्रीन या कट दृश्य के लिए मजबूर करना है। जीसी को पता नहीं चल सकता है कि ऐसा सबसे अच्छा पल कब होगा।
  • सामान्य रूप से विलंबता नियंत्रण । कुछ वेब एप्लिकेशन GCs को अक्षम कर देते हैं और समय-समय पर लोड बैलेंसर रोटेशन से बाहर निकलने के दौरान G2 संग्रह चलाते हैं। इस तरह से G2 विलंबता कभी भी उपयोगकर्ता के सामने नहीं आती है।

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

लक्ष्य संगतता और स्मृति उपयोग की सीमा से ऊपर दिए गए उदाहरण। उन मामलों में प्रेरित जीसी समझ में आ सकते हैं।

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

ध्यान दें, जीसी की लागत ढेर के आकार और ढेर पर संदर्भ की संख्या के साथ भिन्न होती है। एक छोटे से ढेर पर लागत बहुत छोटी हो सकती है। मैंने 1-2GB / सेकंड के .NET 4.5 के साथ G2 संग्रह दरों को 1GB हीप आकार के साथ एक उत्पादन ऐप पर देखा है।


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

3
दूसरे से अंतिम पैराग्राफ के लिए +1। कुछ लोगों को संकलक के बारे में समान भावना है और लगभग कुछ भी "समय से पहले अनुकूलन" कहने के लिए जल्दी है। मैं आमतौर पर उन्हें कुछ इसी तरह बताता हूं।
होनज़ा ब्रेबेक

2
उस पैराग्राफ के लिए भी +1। मुझे यह चौंकाने वाला लगता है कि लोगों को लगता है कि किसी अन्य व्यक्ति द्वारा लिखे गए कंप्यूटर प्रोग्राम को आवश्यक रूप से अपने कार्यक्रम की प्रदर्शन विशेषताओं को खुद से बेहतर समझना चाहिए।
मेहरदाद

1
@HonzaBrabec समस्या दोनों मामलों में समान है: यदि आपको लगता है कि आप जीसी या संकलक से बेहतर जानते हैं, तो अपने आप को चोट पहुंचाना बहुत आसान है। यदि आप वास्तव में अधिक जानते हैं, तो आप केवल तभी अनुकूलन कर सकते हैं जब आप जानते हैं कि यह समय से पहले नहीं है।
’११

27

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

कचरा कलेक्टर को मैन्युअल रूप से आह्वान करने का उपयुक्त समय, तब, जब आप कुछ ऐसा कर रहे हैं जो 1) बहुत कचरा पैदा कर सकता है, और 2) उपयोगकर्ता द्वारा कुछ समय लेने और सिस्टम को गैर जिम्मेदार छोड़ने की उम्मीद है वैसे भी। एक क्लासिक उदाहरण कुछ बड़े लोड करने के अंत में है (एक दस्तावेज, एक मॉडल, एक नया स्तर, आदि)


12

एक बात जिसका किसी ने उल्लेख नहीं किया है, वह यह है कि, जबकि Windows GC आश्चर्यजनक रूप से अच्छा है, Xbox पर GC कचरा है (इच्छित उद्देश्य)

तो जब एक XNA गेम को XBox पर चलाने का इरादा होता है, तो यह उचित समय के लिए कचरा संग्रहण के लिए बिल्कुल महत्वपूर्ण है, या आपके पास भयानक आंतरायिक एफपीएस हिचकी होगी। इसके अतिरिक्त, XBox पर, structएस-वे का उपयोग करना सामान्य है , जिस तरह से आप सामान्य रूप से अधिक बार करते हैं, उन वस्तुओं की संख्या को कम करने के लिए जिन्हें कचरा एकत्र करने की आवश्यकता होती है।


4

कचरा संग्रह सबसे पहले और एक स्मृति प्रबंधन उपकरण है। जैसे, मेमोरी प्रेशर होने पर कचरा संग्रहकर्ता एकत्रित करेगा।

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

हालाँकि , कचरा संग्रहकर्ता आमतौर पर स्मृति के अलावा संसाधनों के उपयोग को अनुकूलित करने का प्रयास नहीं करते हैं। कचरा एकत्र किए गए वातावरणों में, अधिकांश मूल्यवान गैर-मेमोरी संसाधनों में एक closeविधि या समान है, लेकिन कुछ ऐसे अवसर हैं जहां यह किसी कारण के लिए नहीं है, जैसे कि मौजूदा एपीआई के साथ संगतता।

इन मामलों में यह कचरा संग्रह को मैन्युअल रूप से लागू करने के लिए समझ में आता है जब आप जानते हैं कि एक मूल्यवान गैर-स्मृति संसाधन का उपयोग किया जा रहा है।

RMI

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

हालाँकि, एकमात्र तरीका है कि सर्वर को यह पता है कि क्या क्लाइंट इसे बताता है, और क्लाइंट केवल सर्वर को बताता है कि क्लाइंट को जो कुछ भी उपयोग कर रहा है, उसे इकट्ठा करने के बाद उसे किसी ऑब्जेक्ट की आवश्यकता नहीं है।

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

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


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

और जैसा कि उत्तर में कहा गया है, जब एक closeविधि उपलब्ध है (या संसाधन का उपयोग usingब्लॉक के साथ किया जा सकता है ), ये सही दृष्टिकोण हैं। जवाब विशेष रूप से उन दुर्लभ मामलों से संबंधित है जहां ये तंत्र उपलब्ध नहीं हैं।
जेम्स_पिक

मेरी व्यक्तिगत राय है कि कोई भी इंटरफ़ेस जो एक गैर-स्मृति संसाधन का प्रबंधन करता है, लेकिन एक करीबी विधि प्रदान नहीं करता है, एक ऐसा इंटरफ़ेस है जिसका उपयोग नहीं किया जाना चाहिए , क्योंकि इसका मज़बूती से उपयोग करने का कोई तरीका नहीं है।
जूल्स

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

2

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

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

हालांकि कुछ बैच प्रकार के प्रसंस्करण में आप अधिक जानते हैं तो जीसी। उदाहरण के लिए एक आवेदन पर विचार करें।

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

आप प्रत्येक फ़ाइल को संसाधित करने के बाद एक पूर्ण कचरा संग्रहण को बाध्य करने के लिए एक केस (सावधानी से) परीक्षण करने में सक्षम हो सकते हैं।

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

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

मेरे पास एक कचरा संग्रह एपीआई होगा जब मैं अपने जीसी को मजबूर करने के बिना इस प्रकार के बारे में संकेत दे सकता हूं।

" रिको मारियानी का प्रदर्शन टिडबिट्स " भी देखें


2

ऐसे कई मामले हैं जहाँ आप स्वयं को gc () बुलाना चाहते हैं।

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

1
क्या कोई नीच का कारण बता सकता है? मैं खुद ही इस सवाल का जवाब देने के लिए पर्याप्त नहीं हूं (पहली नज़र में यह थोड़े समझ में आता है)।
ओमेगा

1
मुझे लगता है कि आप तीसरे बिंदु के लिए एक नीचा मिल गया है। "यह सिर्फ सामान्य ज्ञान है" कहने के लिए संभावित रूप से भी।
इबिबिस

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

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

2
By calling gc() depending on implementation, the memory in used will be compacted which massively improves cache locality. This will result in massive improve in performance that you will not get from profiling.यदि आप एक पंक्ति में वस्तुओं का एक टन आवंटित करते हैं तो वे पहले से ही संकुचित हो जाते हैं। यदि कुछ भी, कचरा संग्रह उन्हें थोड़ा सा फेरबदल कर सकता है। किसी भी तरह से, डेटा संरचनाओं का उपयोग करना जो घने हैं और स्मृति में बेतरतीब ढंग से इधर-उधर कूदना एक बड़ा प्रभाव डालने वाले हैं। यदि आप भोले-भाले एक-तत्व-प्रति-नोड लिंक्ड सूची का उपयोग कर रहे हैं, तो उसके लिए मैन्युअल जीसी चालबाज़ी की कोई मात्रा नहीं होगी।
डोभाल

2

एक वास्तविक दुनिया उदाहरण:

मेरे पास एक वेब एप्लिकेशन था जो डेटा के एक बहुत बड़े सेट का उपयोग करता था जो शायद ही कभी बदलता था और जिसे बहुत तेज़ी से एक्सेस करने की आवश्यकता होती थी (AJAX के माध्यम से प्रति-कीस्ट्रोक प्रतिक्रिया के लिए त्वरित पर्याप्त)।

यहां स्पष्ट रूप से पर्याप्त बात यह है कि संबंधित ग्राफ़ को मेमोरी में लोड करना है, और डेटाबेस के बजाय इसे वहां से एक्सेस करना है, जब डीबी बदलता है।

लेकिन बहुत बड़ा होने के नाते, एक भोले भार ने भविष्य में बढ़ने के कारण डेटा के साथ कम से कम 6GB मेमोरी ली होगी। (मेरे पास सटीक आंकड़े नहीं हैं, एक बार यह स्पष्ट हो गया था कि मेरी 2 जीबी मशीन कम से कम 6 जीबी के साथ सामना करने की कोशिश कर रही थी मेरे पास सभी माप थे जिन्हें मुझे पता होना चाहिए कि यह काम नहीं करने वाला था)।

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

सभी अच्छी तरह से और अच्छा है, लेकिन इसके लिए अभी भी इस राज्य को प्राप्त करने के लिए लगभग आधे मिनट की जगह में 6GB से अधिक वस्तुओं के माध्यम से मंथन किया गया है। अपने दम पर छोड़ दिया, जीसी सामना नहीं किया; अनुप्रयोग के सामान्य पैटर्न पर गतिविधि में स्पाइक (प्रति सेकंड बहुत कम भारी) बहुत तेज था।

इसलिए GC.Collect()इस निर्माण प्रक्रिया के दौरान समय-समय पर कॉल करने का मतलब था कि पूरी चीज सुचारू रूप से काम करती है। बेशक, मैंने GC.Collect()एप्लिकेशन के चलने के समय को मैन्युअल रूप से कॉल नहीं किया ।

यह वास्तविक दुनिया का मामला हमारे द्वारा उपयोग किए जाने के दिशानिर्देशों का एक अच्छा उदाहरण है GC.Collect():

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

जब मैंने सोचा था कि ज्यादातर समय मेरे पास ऐसा मामला हो सकता है जहां GC.Collect()कॉल करने लायक है, क्योंकि बिंदु 1 और 2 लागू होते हैं, बिंदु 3 ने सुझाव दिया कि इससे चीजें खराब हो गईं या कम से कम चीजें बेहतर नहीं हुईं (और बहुत कम या कोई सुधार नहीं हुआ है) एक आवेदन के जीवनकाल में बेहतर साबित करने के लिए दृष्टिकोण अधिक होने की संभावना के रूप में कॉलिंग पर कॉल न करने की ओर झुकाव)।


0

मेरे पास कचरा निपटान के लिए एक उपयोग है जो कुछ अपरंपरागत है।

यह गुमराह करने वाली प्रथा है जो दुर्भाग्य से, सी # दुनिया में बहुत ही प्रचलित है, बदसूरत, भद्दे, अयोग्य, और त्रुटि वाले आइडियल प्रयोज्य-डिस्पोजिंग के रूप में उपयोग किए जाने वाले ऑब्जेक्ट निपटान को लागू करने के लिए । MSDN लंबाई में यह वर्णन है, और बहुत से लोग यह कसम, यह धार्मिक का पालन करें, घंटे पर घंटे खर्च पर चर्चा ठीक है कि यह कैसे किया जाना चाहिए, आदि

(कृपया ध्यान दें कि क्या मैं यहाँ बदसूरत बोल रहा हूँ है नहीं वस्तु निपटान पैटर्न ही; क्या मैं बदसूरत बोल रहा हूँ विशेष है IDisposable.Dispose( bool disposing )मुहावरा।)

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

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

क्षमा करें, लेकिन यह सिर्फ पागल है।

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

अब, सख्ती से बोलना, यह निश्चित रूप से असंभव है कि कोई भी प्रोग्रामर कभी भी भूल करने की गलती नहीं करेगा IDisposable.Dispose(), लेकिन इस गलती को पकड़ने के लिए हम विध्वंसक का उपयोग कर सकते हैं। यह बहुत सरल है, वास्तव में: सभी विध्वंसक को एक लॉग प्रविष्टि उत्पन्न करनी होती है यदि यह पता लगाता है कि disposedडिस्पोजेबल ऑब्जेक्ट का झंडा कभी सेट नहीं किया गया था true। तो, विध्वंसक का उपयोग हमारी निपटान रणनीति का एक अभिन्न हिस्सा नहीं है, लेकिन यह हमारी गुणवत्ता आश्वासन तंत्र है। और चूंकि यह एक डिबग-मोड केवल टेस्ट है, हम अपने पूरे डिस्ट्रॉक्टर को एक #if DEBUGब्लॉक के अंदर रख सकते हैं , इसलिए हम कभी भी उत्पादन वातावरण में विनाश विनाश नहीं करते हैं। ( IDisposable.Dispose( bool disposing )मुहावरे निर्धारित करते हैं किGC.SuppressFinalize() अंतिम रूप से ओवरहेड को कम करने के लिए ठीक से लागू किया जाना चाहिए, लेकिन मेरे तंत्र के साथ उत्पादन वातावरण पर उस ओवरहेड से पूरी तरह से बचना संभव है।)

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

इसलिए, इस तंत्र के काम करने के लिए, हमारे आवेदन के DEBUG संस्करण को छोड़ने से पहले एक पूर्ण कचरा निपटान करना चाहिए, ताकि यह गारंटी दी जा सके कि सभी विनाशकों को आमंत्रित किया जाएगा, और इस प्रकार किसी भी IDisposableऑब्जेक्ट को पकड़ना है जिसे हम निपटाना भूल गए हैं।


Now, strictly speaking, it is of course impossible to guarantee that no programmer will ever make the mistake of forgetting to invoke IDisposable.Dispose()वास्तव में, यह नहीं है, हालांकि मुझे नहीं लगता कि C # इसके लिए सक्षम है। संसाधन को उजागर न करें; इसके बजाय आप इसे (मूल रूप से, एक सनक) के साथ जो कुछ भी करेंगे, उसका वर्णन करने के लिए एक डीएसएल प्रदान करें, साथ ही एक फ़ंक्शन जो संसाधन का अधिग्रहण करता है, चीजों को करता है, इसे मुक्त करता है और परिणाम देता है। ट्रिक यह सुनिश्चित करने के लिए टाइप सिस्टम का उपयोग करने के लिए है कि यदि कोई संसाधन के संदर्भ में स्मगल करता है, तो इसे रन फ़ंक्शन में किसी अन्य कॉल में उपयोग नहीं किया जा सकता है।
डोभाल

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

-1 खराब सलाह, क्योंकि अंतिम रूप से वास्तव में कैसे काम होता है। मैं dispose(disposing)मुहावरेदार होने पर आपकी बात से पूरी तरह सहमत हूं , लेकिन मैं ऐसा इसलिए कहता हूं क्योंकि लोग अक्सर उस तकनीक और फाइनल का उपयोग करते हैं जब उनके पास केवल प्रबंधित संसाधन होते हैं ( DbConnectionउदाहरण के लिए ऑब्जेक्ट प्रबंधित किया जाता है , यह पिनवॉच या कॉम मार्शल्ड नहीं है), और आप केवल स्वयं का उपयोग कर सकते हैं कभी भी अनधिकृत, पिनकोड, COM मार्शल, या UNSAFE कोड के साथ एक अंतिम आयात । मैं अपने जवाब में ऊपर विस्तार से बताता हूं कि अंतिम रूप से महंगे फाइनल कैसे होते हैं, जब तक आपकी कक्षा में अप्रबंधित संसाधन न हों, उनका उपयोग करें ।
जिमी हॉफ

2
मैं लगभग आपको +1 देना चाहता हूं, हालांकि सिर्फ इसलिए कि आप कुछ कम कर रहे हैं, इसलिए बहुत से लोग dispose(dispoing)मुहावरे में एक महत्वपूर्ण चीज के रूप में लेते हैं , लेकिन सच्चाई यह है कि केवल इतना प्रचलित है क्योंकि लोग जीसी सामान से इतने डरते हैं कि कुछ असंबंधित यह ( disposeजीसी के साथ करने के लिए zilch होना चाहिए) उन्हें केवल जांच के बिना निर्धारित दवा लेने के लिए योग्यता देता है। इसका निरीक्षण करने के लिए आप पर अच्छा है, लेकिन आप सबसे बड़े पूरे से चूक गए (यह फाइनल करने वालों को अधिक से अधिक बार प्रोत्साहित करता है जितना उन्हें होना चाहिए)
जिमी

1
@JimmyHoffa आपके इनपुट के लिए धन्यवाद। मैं मानता हूं कि एक अंतिम रूप से उपयोग किए जाने वाले संसाधनों को जारी करने के लिए सामान्य रूप से केवल एक फाइनल का उपयोग किया जाना चाहिए , लेकिन क्या आप इस बात से सहमत नहीं होंगे कि DEBUG के निर्माण पर यह नियम अनुचित है, और यह कि DEBUG बिल्ड पर हमें बग्स को पकड़ने के लिए फाइनल उपयोग करने के लिए स्वतंत्र होना चाहिए? यह सब मैं यहाँ सुझा रहा हूँ, इसलिए मैं यह नहीं देखता कि आप इसे क्यों लेते हैं। दुनिया के जावा पक्ष पर इस दृष्टिकोण की एक लंबी व्याख्या के लिए programmers.stackexchange.com/questions/288715/… भी देखें ।
माइक नकिस

0

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

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

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

गेम्स के लिए क्रैश इस मायने में बेहतर हो सकता है कि इसे जल्दी से पता लगाया जा सके और तय किया जा सके, लेकिन मिशन-क्रिटिकल प्रोग्राम के लिए, पूरी तरह से अनपेक्षित समय पर क्रैश करना किसी को मार सकता है। इसलिए मुझे लगता है कि मुख्य मामले ऐसे परिदृश्य होंगे जहां दुर्घटना नहीं होती है या कुछ अन्य रूप हैं सुरक्षा बिल्कुल महत्वपूर्ण हैं, और तुलनात्मक रूप से एक तार्किक रिसाव अपेक्षाकृत तुच्छ चीज है।

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

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

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