2009-11-16 14 views
9

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

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

>>> c = django.test.Client() 
>>> c.get('/the/view/') 
gc: collecting generation 0... 
gc: objects in each generation: 724 5748 147341 
gc: done. 
gc: collecting generation 0... 
gc: objects in each generation: 731 6460 147341 
gc: done. 
[...more of the same...]  
gc: collecting generation 1... 
gc: objects in each generation: 718 8577 147341 
gc: done. 
gc: collecting generation 0... 
gc: objects in each generation: 714 0 156614 
gc: done. 
[...more of the same...] 
gc: collecting generation 0... 
gc: objects in each generation: 715 5578 156612 
gc: done. 

, वस्तुओं की एक बड़ी राशि आवंटित किए जाते हैं, लेकिन शुरू में पीढ़ी 1 करने के लिए ले जाया जाता है, और जब जनरल: जब एक ही अनुरोध के लिए

gc.set_debug(gc.DEBUG_STATS) 

का उपयोग कर फिर, मैं निम्नलिखित उत्पादन को देखने के 1 एक ही अनुरोध के दौरान साफ़ हो गया है, वे पीढ़ी 2 में स्थानांतरित हो जाते हैं। यदि मैं मैनुअल gc.collect (2) बाद में करता हूं, तो उन्हें हटा दिया जाता है। और, जैसा कि मैंने उल्लेख किया है, जब भी अगली स्वचालित जीन 2 स्वीप होती है, तो भी हटा दिया जाता है, अगर मैं सही ढंग से समझता हूं, तो इस मामले में प्रत्येक 10 अनुरोधों की तरह कुछ होगा (इस बिंदु पर ऐप को 150 एमबी की आवश्यकता है)।

ठीक है, इसलिए शुरुआत में मैंने सोचा कि एक अनुरोध के प्रसंस्करण के भीतर कुछ चक्रीय संदर्भ हो रहा है जो इन अनुरोधों को संभालने में इन वस्तुओं में से किसी एक को सहेजने से रोकता है। हालांकि, मैंने अनुरोध प्रक्रिया के अंदर और बाद में डिबगिंग के बाद, pympler.muppy और objgraph का उपयोग करके एक खोजने का प्रयास करने में घंटों बिताए हैं, और ऐसा कोई प्रतीत नहीं होता है। इसके बजाय, ऐसा लगता है कि 14.000 ऑब्जेक्ट्स या तो अनुरोध के दौरान बनाए गए हैं, कुछ संदर्भ-वैश्विक वस्तु के संदर्भ में सभी संदर्भ श्रृंखला में हैं, यानी अनुरोध समाप्त होने के बाद, उन्हें मुक्त किया जा सकता है।

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

कि सेटअप के साथ

, यहाँ मेरे सवालों हैं:

  • ऊपर भी कोई मतलब है, या मैं कहीं और समस्या को देखने के लिए है? क्या यह सिर्फ एक दुर्भाग्यपूर्ण दुर्घटना है कि इस विशेष उपयोग के मामले में महत्वपूर्ण डेटा इतने लंबे समय तक रखा जाता है?

  • क्या इस मुद्दे से बचने के लिए मैं कुछ भी कर सकता हूं। मुझे पहले से ही दृश्य को अनुकूलित करने की कुछ संभावनाएं दिखाई दे रही हैं, लेकिन यह सीमित दायरे के साथ एक समाधान प्रतीत होता है - हालांकि मुझे यकीन नहीं है कि मैं जो सामान्य हूं, वह भी होगा; उदाहरण के लिए gc.collect() या gc.set_threshold() को मैन्युअल रूप से कॉल करने के लिए यह सलाह दी जाती है?

कैसे कचरा कलेक्टर ही काम करता है के संदर्भ में:

  • मैं सही ढंग से समझ है कि एक वस्तु हमेशा अगली पीढ़ी के लिए ले जाया गया है एक झाड़ू यह देखता है और निर्धारित करता है कि क्या है कि संदर्भ यह चक्रीय नहीं हैं, लेकिन वास्तव में एक मूल वस्तु के लिए पता लगाया जा सकता है।

  • क्या होगा अगर जीसी एक, कहते हैं, पीढ़ी 1 स्वीप करता है, और एक वस्तु है कि पीढ़ी 2 के भीतर एक वस्तु द्वारा संदर्भित है पाता होता है; क्या यह पीढ़ी 2 के अंदर उस रिश्ते का पालन करता है, या क्या स्थिति का विश्लेषण करने से पहले पीढ़ी 2 स्वीप होने की प्रतीक्षा है?

  • gc.DEBUG_STATS का उपयोग कर, मैं की जानकारी "हर पीढ़ी में वस्तुओं" के बारे में मुख्य रूप से परवाह; हालांकि, मैं सैकड़ों "जीसी: 0.0740s विलुप्त हो रहा हूं।", "जीसी: 1258233035.9370s समाप्त हो गया।" संदेशों; वे पूरी तरह से असुविधाजनक हैं - उनके लिए मुद्रित होने में काफी समय लगता है, और वे दिलचस्प चीजों को ढूंढना बहुत कठिन बनाते हैं। क्या उनसे छुटकारा पाने का कोई तरीका है?

  • मुझे नहीं लगता कि पीढ़ी द्वारा gc.get_objects() करने का कोई तरीका है, यानी केवल पीढ़ी 2 से ऑब्जेक्ट्स को पुनर्प्राप्त करें, उदाहरण के लिए?

उत्तर

2

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

मुझे सुझाव है कि आप अपने दृश्य में gc.collect() पर कॉल करें और देखें कि आपके प्रतिक्रिया समय और आपके मेमोरी उपयोग पर इसका क्या प्रभाव है।

नोट भी this question जो बताता है कि सेटिंग DEBUG=True मेमोरी खाती है जैसे कि यह तिथि के अनुसार लगभग बिक चुकी है।

+0

+1 DEBUG = False सेटिंग का उल्लेख करने के लिए +1 ताकि Django आपके सभी SQL क्वेरी लॉग न करे। – Kekoa

+0

वाह, डैंजो की कितनी मानसिक छवि खाने के लिए खाने का तरीका –

3

क्या ऊपर भी समझ में आता है, या मुझे कहीं और समस्या की तलाश करनी है? क्या यह सिर्फ एक दुर्भाग्यपूर्ण दुर्घटना है कि इस विशेष उपयोग के मामले में महत्वपूर्ण डेटा इतने लंबे समय तक रखा जाता है?

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

क्या इस मुद्दे से बचने के लिए मैं कुछ भी कर सकता हूं। मुझे पहले से ही दृश्य को अनुकूलित करने की कुछ संभावनाएं दिखाई दे रही हैं, लेकिन यह सीमित दायरे के साथ एक समाधान प्रतीत होता है - हालांकि मुझे यकीन नहीं है कि मैं जो सामान्य हूं, वह भी होगा; उदाहरण के लिए gc.collect() या gc.set_threshold() को मैन्युअल रूप से कॉल करने के लिए यह सलाह दी जाती है?

सामान्य सलाह (शायद आप इसे जानते हैं, लेकिन वैसे भी): परिपत्र संदर्भ और ग्लोबल्स से बचें (threading.local सहित)। चक्रों को तोड़ने और ग्लोबल्स साफ़ करने का प्रयास करें जब django डिज़ाइन उनसे बचने के लिए कड़ी मेहनत करता है। gc.get_referrers(obj) आपको ध्यान देने योग्य स्थानों को खोजने में मदद कर सकता है। कचरा कलेक्टर को अक्षम करने और इसे प्रत्येक अनुरोध के बाद मैन्युअल रूप से कॉल करने का एक और तरीका है, जब यह करने का सबसे अच्छा स्थान होता है (इससे वस्तुओं को अगली पीढ़ी तक जाने से रोका जा सकता है)।

मुझे नहीं लगता कि पीढ़ी द्वारा gc.get_objects() को करने का कोई तरीका है, यानी केवल पीढ़ी 2 से वस्तुओं को पुनर्प्राप्त करें, उदाहरण के लिए?

दुर्भाग्यवश gc इंटरफ़ेस के साथ यह संभव नहीं है। लेकिन जाने के कई तरीके हैं। आप सूची के अंत को gc.get_objects() द्वारा लौट सकते हैं, क्योंकि इस सूची में ऑब्जेक्ट्स को पीढ़ी द्वारा क्रमबद्ध किया जाता है।आप कॉल के बीच कमजोर संदर्भों को संग्रहीत करके पिछले कॉल से लौटाए गए एक के साथ सूची की तुलना कर सकते हैं (उदा। WeakKeyDictionary में)। आप अपने स्वयं के सी मॉड्यूल में gc.get_objects() को फिर से लिख सकते हैं (यह आसान है, अधिकतर कॉपी-पेस्ट प्रोग्रामिंग!) क्योंकि वे आंतरिक रूप से पीढ़ी द्वारा संग्रहित होते हैं, या यहां तक ​​कि ctypes के साथ आंतरिक संरचनाओं तक पहुंच सकते हैं (काफी गहरी ctypes समझने की आवश्यकता है)।

+0

get_objects() सॉर्ट किया जा रहा है, संकेत के लिए धन्यवाद, काफी अच्छा है। – miracle2k

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