2011-03-31 7 views
22

सबसे पहले, DEBUG = False सेटिंग्स.py में, इसलिए नहीं, connections['default'].queries तब तक बढ़ रहा है जब तक यह सभी मेमोरी का उपयोग नहीं करता है।डीजेंगो में बड़े प्रश्नों (या प्रश्नों की श्रृंखला) के बाद सिस्टम को स्मृति क्यों जारी नहीं किया जाता है?

इस तथ्य से शुरुआत करें कि मैंने User तालिका django.contrib.auth.models.User से 10000 उपयोगकर्ताओं के साथ लोड की है (प्रत्येक नाम 'परीक्षण #' जहां # 1 और 10000 के बीच एक संख्या है)।

यहाँ दृश्य है:

from django.contrib.auth.models import User 
from django.http import HttpResponse 

import time 

def leak(request): 
    print "loading users" 

    users = [] 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 
    users += list(User.objects.all()) 

    print "sleeping" 
    time.sleep(10) 

    return HttpResponse('') 

मैं /leak/ यूआरएल के लिए ऊपर दृश्य जुड़ा हुआ है और (डीबग = झूठी साथ विकास सर्वर प्रारंभ है, और मैं परीक्षण किया है और यह कुछ भी नहीं गया है अन्य उदाहरण बनाम विकास सर्वर चलाने के साथ करें)।

चलाने के बाद:

% curl http://localhost:8000/leak/ 

runserver प्रक्रिया 'स्मृति आकार के नीचे ps aux उत्पादन से देखा चारों ओर करने के लिए बढ़ता है और फिर उस स्तर पर रहता है।

USER  PID %CPU %MEM VSZ RSS TTY  STAT START TIME COMMAND 
dlamotte 25694 11.5 34.8 861384 705668 pts/3 Sl+ 19:11 2:52 /home/dlamotte/tmp/django-mem-leak/env/bin/python ./manage.py runserver 

फिर ऊपर curl आदेश ऊपर चल रहा है विकसित करने के लिए उदाहरण के स्मृति उपयोग (जो मैं एक सच्चे स्मृति रिसाव से उम्मीद?), इसे फिर से उपयोग करना चाहिए स्मृति प्रतीत नहीं होता? हालांकि, मुझे लगता है कि यहां कुछ गड़बड़ है कि स्मृति को सिस्टम में रिलीज़ नहीं किया जाता है (हालांकि, मैं समझता हूं कि यह बेहतर प्रदर्शन हो सकता है कि पायथन स्मृति को जारी नहीं करता है)।

इसके बाद, मैंने यह देखने का प्रयास किया कि क्या पाइथन आवंटित स्मृति के बड़े हिस्से को जारी करेगा या नहीं। तो मैं एक अजगर सत्र से निम्नलिखित का प्रयास:

>>> a = '' 
>>> a += 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' * 10000000 
>>> del a 

स्मृति a += ... लाइन के रूप में उम्मीद पर आवंटित किया जाता है, लेकिन जब del a होता है, स्मृति जारी किया गया है। Django क्वेरी सेट के लिए व्यवहार अलग क्यों है? क्या ऐसा कुछ है जो डीजेंगो करना चाहता है? क्या इस व्यवहार को बदलने का कोई तरीका है?

मैंने सचमुच इस व्यवहार को डिबग करने में 2 दिन बिताए हैं, इस बारे में कोई विचार नहीं है कि मुझे आगे जाना है (मैंने गप्पी और ओबजग्राफ का उपयोग करना सीखा है जो कि मुझे पता चल सकता है कि कुछ भी दिलचस्प नहीं है)।

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

अद्यतन: अजगर संस्करण आवेदनों की 2.6.5

+0

+1। इस सवाल के जवाब में बहुत दिलचस्पी है। मैंने डीजेगो के साथ उत्पादन पर कुछ समय के साथ अजीब स्मृति रिसाव भी पहचाना (DEBUG = झूठा भी और यहां तक ​​कि बहुत ही सरल ऐप्स/परियोजनाओं के साथ)। –

+0

यदि आप 'डेल उपयोगकर्ता' हैं तो क्या होता है? –

+0

पाइथन का कौन सा संस्करण आप उपयोग कर रहे हैं (सर्वर और कमांड लाइन परीक्षण दोनों के लिए)? पुराने संस्करण सिस्टम पर ऑब्जेक्ट्स के लिए आवंटित स्मृति को रिलीज़ नहीं करते हैं, इसलिए यदि आपका स्थानीय संस्करण 2.5 या बाद वाला है, लेकिन आपका सर्वर 2.4 चल रहा है, तो यह आपकी समस्या हो सकती है। एकल बड़े आवंटन (आपकी बड़ी स्ट्रिंग की तरह) आवंटक को भी बाईपास कर सकता है - देखें कि '([[]] * 10 ** 6) जैसे कुछ के साथ क्या होता है। – ncoghlan

उत्तर

24

मैंने चीजों को स्पष्ट करने के लिए मेरी टिप्पणियों को उत्तर में बदलने का फैसला किया।

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

निम्नलिखित प्रयोग (मैं 3.2 इस्तेमाल किया, लेकिन 2.5+ समान होना चाहिए अगर आप xrange का उपयोग करें) का प्रयास करें:

# Create the big lists in advance to avoid skewing the memory counts 
seq1 = [None] * 10**6 # Big list of references to None 
seq2 = seq1[::10] 

# Create and reference a lot of smaller lists 
seq1[:] = [[] for x in range(10**6)] # References all the new lists 
seq2[:] = seq1[::10] # Grab a second reference to 10% of the new lists 

# Memory fragmentation in action 
seq1[:] = [None] * 10**6 # 90% of the lists are no longer referenced here 
seq2[:] = seq1[::10] # But memory freed only after last 10% are dropped 

ध्यान दें, भले ही आप, seq1 और seq2 के लिए संदर्भ ड्रॉप ऊपर अनुक्रम संभावना होगा अपनी पाइथन प्रक्रिया को बहुत सारी अतिरिक्त मेमोरी छोड़ दें।

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

+0

ड्रॉपबॉक्स टीम ने वास्तव में अपने पिकॉन टॉक में इस समस्या का वास्तव में अच्छा वर्णन दिया (पहली बार 18:00 बजे दिखाए गए स्लाइड में उल्लिखित, ~ 26: 00 पर अधिक जानकारी): http://pycon.blip.tv/file/4878722/ – ncoghlan

+0

अच्छा जवाब। बहुत अच्छी व्याख्या। धन्यवाद! – dlamotte

+0

क्या आप स्रोत के प्रासंगिक हिस्सों से लिंक कर सकते हैं? –

5

बहुत सारे, भाषा runtimes, और शायद यह भी कुछ सिस्टम स्मृति allocators का उपयोग करते हुए एक दृश्य के साथ जब तक संभव हो के लिए जगह में deallocated स्मृति रखेंगे के लिए पुन: उपयोग करते हुए यह, पूरी तरह प्रदर्शन उद्देश्यों के लिए। Django जैसी जटिल प्रणाली में यह संभवतः सी में कार्यान्वित किया जा सकता है, जो इस व्यवहार को प्रदर्शित करता है, या यह किसी प्रकार के मेमोरी पूल, या आलसी कचरा संग्रह के साथ पाइथन हो सकता है।

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

हालांकि, प्रारंभिक आवंटन और डेलोक के बाद आवंटन प्रक्रिया को दोहराने से स्मृति की मात्रा को दोगुना नहीं किया जाता है, जो आप देख रहे हैं वह स्मृति रिसाव नहीं है बल्कि मेमोरी पूलिंग है। यह केवल एक समस्या होने की संभावना है यदि आपके पास ऐसी कई प्रक्रियाएं हैं जो उस मशीन पर सीमित मात्रा में स्मृति के लिए संघर्ष करती हैं।

+0

फ़ंक्शन के दायरे को छोड़ने जैसा ही है, अच्छा जवाब है, बस" हाथ से बचने "जादू से स्पष्टीकरण के बहुत से रोमांचित नहीं है ... मुझे वह पसंद है जो ncoghlan ने दिया मुझे समस्या दोहराने का एक उदाहरण है, – dlamotte

+0

हालांकि आपके इनपुट के लिए धन्यवाद, अफसोस की बात है कि कुछ हाथों की बचत आवश्यक है, क्योंकि यह बहुत दुर्लभ है कि आपको उच्च स्तर की भाषा में संसाधन आवंटन के बीच पूरी तरह से निर्धारणात्मक मानचित्रण मिलता है और स्मृति उपयोग को संसाधित करता है इन दिनों ओएस स्तर - विभिन्न मेमोरी प्रबंधकों के लिए बहुत अधिक अनुकूलन हैं। यही कारण है कि आप अकेले 'ps' या कार्य प्रबंधक से स्मृति रिसाव का निदान नहीं कर सकते हैं। – Kylotan

+0

ncoghlan ने इसे इस तरह से समझाया है मेमोरी खंडित हो जाती है और इसलिए दुभाषिया द्वारा मुक्त नहीं किया जा सकता है। मुझे यकीन है कि स्मृति प्रबंधक कुछ बिंदु पर खेलेंगे ... लेकिन मुझे लगता है कि आपने इसे उड़ा दिया है समानुपाती न होना। तथ्य यह है कि, स्मृति को खंडित किया गया था और उसके मुकाबले कुछ भी गहराई से करने के लिए कुछ भी नहीं है। – dlamotte

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