2008-08-26 19 views
27

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

उत्तर

42

संक्षिप्त उत्तर: जावा कचरा संग्रह एक बहुत ही बारीक ट्यूनेड टूल है। System.gc() एक स्लेज-हथौड़ा है।

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

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

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

सूर्य दस्तावेज़ को आप के बारे में सोच रहे थे यहाँ है: Java SE 6 HotSpot™ Virtual Machine Garbage Collection Tuning

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

0

जीसी चीजों को सही तरीके से अंतिम रूप देने के लिए बहुत अनुकूलन करता है।

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

1

माना जाता है कि अंतिमकर्ता उनके .NET नामक के समान होते हैं, तो आपको केवल इन्हें कॉल करने की आवश्यकता होती है जब आपके पास फाइल हैंडल जैसे संसाधन हैं जो रिसाव कर सकते हैं। अधिकांश समय आपके ऑब्जेक्ट्स में इन संदर्भ नहीं होते हैं इसलिए उन्हें कॉल करने की आवश्यकता नहीं होती है।

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

यदि आप आंतरिक रूप से जीसी के बारे में और अधिक जानते थे तो आप अधिक जानकारी प्राप्त करने में सक्षम हो सकते हैं, लेकिन फिर आपने जीसी के लाभों को याद किया है।

4

अंतिमकर्ताओं से परेशान न करें।

वृद्धिशील कचरा संग्रह पर स्विच करें।

यदि आप कचरा कलेक्टर की मदद करना चाहते हैं, तो उन ऑब्जेक्ट्स के संदर्भों को बंद कर दें जिनकी आपको अब आवश्यकता नहीं है। पालन ​​करने के लिए कम रास्ता = अधिक स्पष्ट रूप से कचरा।

यह मत भूलना कि (गैर स्थैतिक) आंतरिक वर्ग के उदाहरण उनके मूल वर्ग उदाहरण के संदर्भ रखते हैं। तो एक आंतरिक वर्ग धागा आप की अपेक्षा से अधिक सामान रखता है।

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

नरम संदर्भों का उपयोग करने पर विचार करें। यदि आपको नहीं पता कि नरम संदर्भ क्या हैं, तो java.lang.ref.SoftReference

फ़ैंटम संदर्भों और कमजोर संदर्भों को स्पष्ट करें जब तक आप वास्तव में उत्साहित न हों।

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

नहीं, मैं मजाक नहीं कर रहा हूं।

संदर्भ कार्यान्वयन डाउनलोड करने के लिए स्वतंत्र है और सूर्य से पीटर डिबल्स पुस्तक वास्तव में अच्छी पढ़ाई है।

+0

मैं 'WeakReference' के लिए उपयोग केस को 'सॉफ्ट रेफरेंस' के लिए एक से अधिक मजबूत मानता हूं। यदि 'फू' को फू के लाभ के लिए 'बार' के संदर्भ की आवश्यकता है, तो इसे एक मजबूत संदर्भ का उपयोग करना चाहिए। यदि 'फू' को बार के लाभ के लिए 'बार' के संदर्भ की आवश्यकता है, तो इसे कमजोर संदर्भ का उपयोग करना चाहिए। उदाहरण के लिए, 'बार' प्रत्येक बार 'Foo' कुछ करता है, अधिसूचित होना चाहता है, लेकिन' Foo 'उतना ही खुश होगा अगर उसे किसी को सूचित नहीं करना पड़ेगा। यदि 'बार' का एकमात्र संदर्भ उन चीज़ों द्वारा आयोजित किया जाता है जो वास्तव में परवाह नहीं करते हैं, तो यह अस्तित्व में नहीं होना चाहिए। – supercat

0

अंतिमकर्ता से बचें। इस बात की कोई गारंटी नहीं है कि उन्हें समय-समय पर बुलाया जाएगा। मेमोरी मैनेजमेंट सिस्टम (यानी, कचरा कलेक्टर) से पहले काफी समय लग सकता है, एक ऑब्जेक्ट को फाइनेंजर के साथ इकट्ठा करने का फैसला करता है।

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

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

3

जहाँ तक finalizers जाना के रूप में:

  1. वे लगभग बेकार हैं। उन्हें समय पर फैशन में जाने की गारंटी नहीं है, या वास्तव में, बिल्कुल (अगर जीसी कभी नहीं चलता है, न तो कोई फाइनलर होगा)। इसका मतलब है कि आप आम तौर पर उन पर भरोसा नहीं करना चाहिए।
  2. अंतिमकर्ताओं को बेवकूफ होने की गारंटी नहीं है। कचरा कलेक्टर यह गारंटी देने के लिए बहुत सावधानी बरतता है कि वह कभी भी उसी वस्तु पर finalize() से अधिक कॉल नहीं करेगा। अच्छी तरह से लिखित वस्तुओं के साथ, इससे कोई फर्क नहीं पड़ता, लेकिन खराब लिखित वस्तुओं के साथ, कई बार अंतिम रूप देने को कॉल करने से समस्याएं हो सकती हैं (उदाहरण के लिए मूल संसाधन ... दुर्घटना की डबल रिलीज)।
  3. प्रत्येक ऑब्जेक्ट जिसमें finalize() विधि है, उसे close() (या समान) विधि भी प्रदान करनी चाहिए। यह वह कार्य है जिसे आपको कॉल करना चाहिए। उदाहरण के लिए, FileInputStream.close()finalize() पर कॉल करने का कोई कारण नहीं है जब आपके पास है जो आपके द्वारा बुलाए जाने का इरादा है।
0

आप बिल्कुल अंतिम विधि को कॉल नहीं करते हैं।

1

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

तो मैं एक अनुमानित व्यवस्थित ढंग से हैंडल को स्पष्ट रूप से बंद करने के लिए हूं। मूल रूप से संसाधनों के साथ काम कर पैटर्न का पालन करना चाहिए के लिए कोड:

SomeStream s = null; 
... 
try{ 
    s = openStream(); 
    .... 
    s.io(); 
    ... 
} finally { 
    if (s != null) { 
     s.close(); 
     s = null; 
    } 
} 

यह और भी जटिल हो जाता है, तो आप अपने खुद के वर्गों कि JNI और खुले हैंडल के माध्यम से काम करते हैं लिखें। आपको यह सुनिश्चित करने की ज़रूरत है कि हैंडल बंद हो जाएं (जारी) और यह केवल एक बार होगा। डेस्कटॉप जे 2 एसई में अक्सर अनदेखा ओएस हैंडल Graphics[2D] है। यहां तक ​​कि BufferedImage.getGrpahics() संभावित रूप से आपको ऐसे हैंडल को वापस कर सकता है जो वीडियो ड्राइवर में इंगित करता है (वास्तव में जीपीयू पर संसाधन धारण करता है)। यदि आप इसे स्वयं जारी नहीं करेंगे और इसे काम करने के लिए कचरा कलेक्टर छोड़ देंगे - आपको अजीब आउटऑफमेमरी और समान स्थिति मिल सकती है जब आप वीडियो कार्ड मैप किए गए बिटमैप्स से बाहर निकलते हैं लेकिन अभी भी बहुत सारी मेमोरी है। मेरे अनुभव में यह अक्सर ग्राफिक्स ऑब्जेक्ट्स (थंबनेल निकालने, स्केलिंग, इसे नाम देने के लिए तेज़) के साथ काम करने वाले तंग लूप में होता है।

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

हैप्पी कोडिंग।

शांति।

संबंधित मुद्दे