2009-10-29 20 views
27

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

  • प्रत्येक परीक्षण को एक अलग थ्रेड सहायता में चलाना होगा?
  • क्या रिसाव के प्रभाव को अलग करने के कोई अन्य तरीके हैं? एक प्रयोग के धावक और वास्तविक प्रयोग कोड: विशिष्ट स्थिति पर

विस्तार

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

अद्यतन

Gnibbler का जवाब वास्तव में मुझे अनुमति दी गई है करने के लिए काम नहीं किया पता लगाएं कि मेरी क्लोजेनेस कैलकुलेशन ऑब्जेक्ट्स जो स्टोर करती हैं, प्रत्येक गणना के दौरान उपयोग किए गए डेटा के सभी को मार नहीं किया जा रहा है। मैंने तब कुछ लिंक को मैन्युअल रूप से हटाने के लिए उपयोग किया जो कि स्मृति समस्याओं को ठीक करता है।

+2

पायथन में "मेमोरी लीक" परिभाषित करें। – hasen

+0

मेरा मतलब है, आप किसी भी स्मृति को मुक्त करने के लिए संभवतः "भूल" नहीं सकते; यह जीसीड है। – hasen

+0

आप कैसे बता सकते हैं कि आपके पास मेमोरी रिसाव है? क्या यह है कि आपकी प्रक्रिया मेमोरी एक बड़े आकार में बढ़ती है, और कभी घटती नहीं है? यदि ऐसा है, तो सलाह दीजिये कि पायथन आवश्यक रूप से ओएस को स्मृति वापस नहीं लौटाता क्योंकि यह अब इसका उपयोग नहीं कर रहा है। –

उत्तर

49

आप स्मृति नीचे ट्रैक करने में मदद करने के लिए कुछ इस तरह उपयोग कर सकते हैं लीक

>>> from collections import defaultdict 
>>> from gc import get_objects 
>>> before = defaultdict(int) 
>>> after = defaultdict(int) 
>>> for i in get_objects(): 
...  before[type(i)] += 1 
... 

अब लगता परीक्षण कुछ स्मृति

>>> leaked_things = [[x] for x in range(10)] 
>>> for i in get_objects(): 
...  after[type(i)] += 1 
... 
>>> print [(k, after[k] - before[k]) for k in after if after[k] - before[k]] 
[(<type 'list'>, 11)] 

11 लीक क्योंकि हम 10 से अधिक सूचियों

युक्त एक सूची लीक हो
+1

वाह खोजने में कामयाब रहा, यह बहुत उपयोगी है। हालांकि, यह वास्तव में मेमोरी लीक थ्रेड्स को कैसे मिला है – Casebash

+1

क्या वस्तुओं की तुलना करने से पहले कचरा संग्रह करने लायक है? – Casebash

+0

धन्यवाद, मैं समस्या का समाधान करने के लिए इसका उपयोग करने में कामयाब रहा। – Casebash

2

मैं केवल व्यक्तिगत कार्यों में प्रयोगों को दोबारा प्रतिक्रिया दूंगा (यदि पहले से ऐसा नहीं है) तो कमांड लाइन से एक प्रयोग संख्या स्वीकार करें जो एकल प्रयोग फ़ंक्शन को कॉल करती है।

अभी bodgy एक खोल स्क्रिप्ट इस प्रकार है:

#!/bin/bash 

for expnum in 1 2 3 4 5 6 7 8 9 10 11 ; do 
    python youProgram ${expnum} otherParams 
done 

इस तरह, आप अपने कोड के अधिकांश के रूप में-है और यह किसी भी मेमोरी लीक आपको लगता है कि आप प्रत्येक प्रयोग के बीच में है बाहर साफ हो जाएगा छोड़ सकते हैं ।

बेशक, सबसे अच्छा समाधान हमेशा किसी समस्या के मूल कारण को ढूंढना और ठीक करना है, जैसा कि आपने पहले ही कहा है, यह आपके लिए एक विकल्प नहीं है।

हालांकि यह अजगर में एक स्मृति रिसाव कल्पना करना मुश्किल है, मुझे लगता है कि एक पर अपने शब्द ले लेंगे - आप फिर भी, कम से कम संभावना है कि तुम वहाँ गलत कर रहे हैं पर विचार कर सकते। एक अलग प्रश्न में बढ़ाने पर विचार करें, कुछ ऐसा जो हम कम प्राथमिकता पर काम कर सकते हैं (इस त्वरित-ठीक संस्करण के विपरीत)।

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

+0

मैंने केवल एक शेल स्क्रिप्ट लिखने पर विचार किया था, लेकिन दुर्भाग्य से मेरा प्रयोगात्मक कोड – Casebash

+1

@paxdiablo से कहीं अधिक जटिल है: मैं मानता हूं कि यह उत्तर छोड़ा जाना चाहिए क्योंकि यह किसी अन्य व्यक्ति के लिए उपयोगी हो सकता है जो इस प्रश्न पर जाता है – Casebash

4

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

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

या, अधिक दृढ़ता से, सुनिश्चित करें कि प्रत्येक परीक्षण पूरा हो जाने पर इसकी पहचान किसी फ़ाइल में एक प्रसिद्ध नाम के साथ संलग्न की जाती है। जब प्रोग्राम शुरू होता है तो यह उस फ़ाइल को पढ़कर शुरू होता है और इस प्रकार जानता है कि कौन से परीक्षण पहले ही चल चुके हैं। यह आर्किटेक्चर अधिक ठोस है क्योंकि यह उस मामले को भी शामिल करता है जहां एक परीक्षण के दौरान प्रोग्राम क्रैश क्रैश होता है; बेशक, इस तरह के दुर्घटनाओं से वसूली को पूरी तरह से स्वचालित करने के लिए, आप एक अलग वॉचडॉग कार्यक्रम और परीक्षण कार्यक्रम के एक नए उदाहरण को शुरू करने के प्रभारी होने की प्रक्रिया चाहते हैं, जब यह निर्धारित करता है कि पिछला एक क्रैश हो गया है (यह उद्देश्य के लिए subprocess का उपयोग कर सकता है - अनुक्रम समाप्त होने पर यह बताने का एक तरीका भी आवश्यक है, उदाहरण के लिए परीक्षण कार्यक्रम से सामान्य निकास का अर्थ यह हो सकता है कि किसी भी क्रैश या स्थिति के साथ बाहर निकलने पर! = 0 नया ताजा उदाहरण शुरू करने की आवश्यकता को इंगित करता है)।

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

+0

धन्यवाद के लिए धन्यवाद प्रस्ताव, लेकिन मैं लीक – Casebash

2

मुझे एक तीसरी पार्टी सी लाइब्रेरी के साथ एक ही समस्या थी जो लीक हो रही थी। सबसे साफ काम-आसपास मैं सोच सकता था कि कांटा और इंतजार करना था। इसका लाभ यह है कि आपको प्रत्येक रन के बाद एक अलग प्रक्रिया भी नहीं बनाना है। आप अपने बैच के आकार को परिभाषित कर सकते हैं।

यहाँ एक सामान्य समाधान है (अगर तुम कभी रिसाव मिल जाए, केवल परिवर्तन करने की आवश्यकता रन() run_single_process (कॉल करने के लिए) के बजाय run_forked() बदलने के लिए है और आपका काम हो जाएगा):

import os,sys 
batchSize = 20 

class Runner(object): 
    def __init__(self,dataFeedGenerator,dataProcessor): 
     self._dataFeed = dataFeedGenerator 
     self._caller = dataProcessor 

    def run(self): 
     self.run_forked() 

    def run_forked(self): 
     dataFeed = self._dataFeed 
     dataSubFeed = [] 
     for i,dataMorsel in enumerate(dataFeed,1): 
      if i % batchSize > 0: 
       dataSubFeed.append(dataMorsel) 
      else: 
       self._dataFeed = dataSubFeed 
       self.fork() 
       dataSubFeed = [] 
       if self._child_pid is 0: 
        self.run_single_process() 
       self.endBatch() 

    def run_single_process(self) 
     for dataMorsel in self._dataFeed: 
      self._caller(dataMorsel) 

    def fork(self): 
     self._child_pid = os.fork() 

    def endBatch(self): 
     if self._child_pid is not 0: 
      os.waitpid(self._child_pid, 0) 
     else: 
      sys.exit() # exit from the child when done 

यह बाल प्रक्रिया में स्मृति रिसाव को अलग करता है। और यह बैच आकार परिवर्तक के मूल्य से अधिक बार कभी रिसाव नहीं करेगा।

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