मैं स्पष्ट रूप से पायथन में स्मृति को कैसे मुक्त कर सकता हूं?


387

मैंने एक पायथन प्रोग्राम लिखा था जो एक बड़ी इनपुट फ़ाइल पर कुछ मिलियन ऑब्जेक्ट्स को त्रिकोण का प्रतिनिधित्व करने के लिए बनाता है। एल्गोरिथ्म है:

  1. एक इनपुट फ़ाइल पढ़ें
  2. फ़ाइल को संसाधित करें और त्रिकोण की एक सूची बनाएं, उनके कोने द्वारा दर्शाया गया है
  3. OFF प्रारूप में कोने का आउटपुट: त्रिकोण की सूची के बाद कोने की सूची। त्रिभुजों को सूचकांकों द्वारा वर्टिकल की सूची में दर्शाया गया है

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

पायथन को यह बताने का सबसे अच्छा तरीका क्या है कि मुझे अब कुछ डेटा की आवश्यकता नहीं है, और इसे मुक्त किया जा सकता है?


11
एक मध्यवर्ती फ़ाइल में त्रिकोणों को प्रिंट क्यों नहीं करते हैं, और जब आपको उनकी आवश्यकता होती है, तो उन्हें फिर से पढ़ते हैं?
ऐलिस परसेल

2
यह प्रश्न संभावित रूप से लगभग दो अलग-अलग चीजों का हो सकता है। क्या वे वही पायथन प्रक्रिया से त्रुटियां हैं , जिस स्थिति में हम स्मृति को पायथन प्रक्रिया के ढेर से मुक्त करने की परवाह करते हैं, या क्या वे सिस्टम पर विभिन्न प्रक्रियाओं से हैं, जिस स्थिति में हम ओएस को स्मृति मुक्त करने की परवाह करते हैं?
चार्ल्स डफी

जवाबों:


453

पायथन ऑफिशियल डॉक्यूमेंटेशन के अनुसार , आप गारबेज कलेक्टर को अप्रतिबंधित मेमोरी जारी करने के लिए बाध्य कर सकते हैं gc.collect()। उदाहरण:

import gc
gc.collect()

19
कुछ असामान्य मामलों को छोड़कर, चीजें वैसे भी अक्सर एकत्रित होती हैं, इसलिए मुझे नहीं लगता कि इससे बहुत मदद मिलेगी।
लेनार्ट रेगेब्रो

24
सामान्य तौर पर, gc.collect () से बचा जाना है। कचरा उठाने वाला अपना काम करना जानता है। जैसा कि कहा गया है, अगर ओ पी एक स्थिति है जहाँ वह अचानक एक deallocating में है बहुत कुछ (मिलियन में) की तरह वस्तुओं की, gc.collect उपयोगी साबित हो सकता।
जेसन बेकर

164
वास्तव में gc.collect()अपने आप को एक लूप के अंत में कॉल करना स्मृति को टुकड़े करने से बचने में मदद कर सकता है, जो बदले में प्रदर्शन को बनाए रखने में मदद करता है। मैंने देखा है कि यह एक महत्वपूर्ण अंतर है (~ 20% रनटाइम IIRC)
रोबोम

38
मैं अजगर 3.6 का उपयोग कर रहा हूं। gc.collect()HDf5 (500k पंक्तियों) से एक पांडा डेटाफ्रेम लोड करने के बाद कॉलिंग ने मेमोरी का उपयोग 1.7GB से घटाकर 500MB कर दिया
जॉन

15
मुझे 32GB मेमोरी वाले सिस्टम में 25GB के कई संख्यात्मक सरणियों को लोड करने और संसाधित करने की आवश्यकता है। का उपयोग कर del my_arrayके बाद gc.collect()के बाद सरणी प्रसंस्करण एक ही रास्ता स्मृति वास्तव में जारी की है और मेरी प्रक्रिया अगले सरणी लोड करने के लिए चलती है।
डेविड

113

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

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

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


31
मुझे यकीन है कि इसका एक तुच्छ उदाहरण देखना चाहते हैं।
हारून हॉल

3
गंभीरता से। @AaronHall ने क्या कहा।
नोब सैबोट

17
@AaronHall तुच्छ उदाहरण अब उपलब्ध , का उपयोग कर multiprocessing.Managerफ़ाइलें बजाय साझा राज्य को लागू करने।
user4815162342

48

delबयान का हो सकता है, लेकिन IIRC यह स्मृति को मुक्त करने की गारंटी नहीं हैडॉक्स यहाँ हैं ... और एक कारण है कि यह जारी नहीं किया गया है यहाँ है

मैंने लिनक्स और यूनिक्स-टाइप सिस्टम पर लोगों को कुछ काम करने, परिणाम प्राप्त करने और फिर इसे मारने के लिए एक अजगर प्रक्रिया के लिए धन्यवाद सुना है।

इस लेख में पायथन कचरा संग्राहक पर नोट्स हैं, लेकिन मुझे लगता है कि मेमोरी कंट्रोल की कमी प्रबंधित मेमोरी के लिए नकारात्मक है


क्या इस समस्या से बचने के लिए आयरनपायथॉन और जाइथन एक और विकल्प होंगे?
एस्टेबन कुयबर

@voyager: नहीं, यह नहीं होगा। और न ही कोई अन्य भाषा, वास्तव में। समस्या यह है कि वह एक सूची में बड़ी मात्रा में डेटा पढ़ता है, और मेमोरी के लिए डेटा बहुत बड़ा है।
लेनार्ट रेगेब्रो

1
यह संभवतः आयरनपीथॉन या जाइथॉन के तहत खराब होगा । उन वातावरणों में, आपको यह भी गारंटी नहीं दी जाती है कि मेमोरी जारी की जाएगी यदि कोई और संदर्भ नहीं है।
जेसन बेकर

@voyager, हाँ, क्योंकि जावा वर्चुअल मशीन मेमोरी को फ्री में करने के लिए विश्व स्तर पर दिखती है। JVM के लिए, Jython कुछ खास नहीं है। दूसरी ओर, JVM की अपनी कमियां हैं, उदाहरण के लिए, आपको पहले से ही यह घोषित करना होगा कि यह कितने बड़े ढेर का उपयोग कर सकता है।
प्रो। फाल्कन कॉन्ट्रैक्ट

32

पायथन कचरा-एकत्र किया जाता है, इसलिए यदि आप अपनी सूची का आकार कम करते हैं, तो यह स्मृति को पुनः प्राप्त करेगा। आप किसी चर से पूरी तरह छुटकारा पाने के लिए "डेल" स्टेटमेंट का उपयोग कर सकते हैं:

biglist = [blah,blah,blah]
#...
del biglist

18
यह सच नहीं है। सूची के आकार को कम करते हुए, मेमोरी को पुनः प्राप्त करने की अनुमति देता है, यह कब होगा इसकी कोई गारंटी नहीं है।
user142350

3
नहीं, लेकिन आमतौर पर इससे मदद मिलेगी। हालाँकि, जैसा कि मैं यहाँ प्रश्न को समझता हूँ, समस्या यह है कि उसके पास इतनी सारी वस्तुएँ हैं कि वह उन सभी को संसाधित करने से पहले स्मृति से बाहर चला जाता है, यदि वह उन्हें एक सूची में पढ़ता है। प्रसंस्करण पूरा होने से पहले सूची को हटाना उपयोगी समाधान होने की संभावना नहीं है। ;)
लेनार्ट रेगेब्र

3
कम-स्मृति / आउट-ऑफ-मेमोरी स्थिति कचरा कलेक्टर के "आपातकालीन रन" को ट्रिगर नहीं करेगी?
जेरेमी फ्रेज़र

4
क्या बड़ी सूची = [] रिलीज़ मेमोरी?
नवयुगल

3
हां, यदि पुरानी सूची में किसी और चीज का संदर्भ नहीं है।
नेड बाचेल्ड जुले

22

आप स्पष्ट रूप से मुक्त स्मृति नहीं कर सकते। आपको यह सुनिश्चित करने की आवश्यकता है कि आप वस्तुओं का संदर्भ नहीं रखते हैं। वे फिर कचरा इकट्ठा करेंगे, स्मृति को मुक्त करेंगे।

आपके मामले में, जब आपको बड़ी सूचियों की आवश्यकता होती है, तो आपको आमतौर पर कोड को पुनर्गठित करने की आवश्यकता होती है, आमतौर पर इसके बजाय जनरेटर / पुनरावृत्तियों का उपयोग करना। इस तरह आपको स्मृति में बड़ी सूचियों की आवश्यकता नहीं है।

http://www.prasannatech.net/2009/07/introduction-python-generators.html


1
यदि यह दृष्टिकोण संभव है, तो यह शायद करने योग्य है। लेकिन यह ध्यान दिया जाना चाहिए कि आप पुनरावृत्तियों पर रैंडम एक्सेस नहीं कर सकते हैं, जिससे समस्याएं हो सकती हैं।
जेसन बेकर

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

आप आसानी से एक पुनरावृत्ति का उपयोग किसी अन्य पुनरावृत्त के यादृच्छिक सबसेट को निकालने के लिए कर सकते हैं।
S.Lott

सच है, लेकिन फिर आपको सबसेट के लिए सबटेट करना होगा, जो बहुत धीमा होगा।
लेन्नर्ट रेगेब्र

21

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

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


एह? CPython का कचरा संग्रह refcounting-based है; यह एक आवधिक निशान-और-झाडू नहीं है (कई सामान्य JVM कार्यान्वयनों के लिए), लेकिन इसके तुरंत बाद कुछ पल को हटा देता है जब इसकी संदर्भ संख्या शून्य हो जाती है। केवल चक्र (जहां पर रिफंड की संख्या शून्य होगी, लेकिन संदर्भ वृक्ष में छोरों के कारण नहीं हैं) को समय-समय पर रखरखाव की आवश्यकता होती है। delऐसा कुछ भी नहीं है जो किसी वस्तु को संदर्भित करने वाले सभी नामों के लिए एक अलग मूल्य प्रदान करता है।
चार्ल्स डफी

मैं देखता हूं कि आप कहां से आ रहे हैं: मैं तदनुसार उत्तर को अपडेट करूंगा। मैं समझता हूं कि सीपीथॉन दुभाषिया वास्तव में कुछ मध्यवर्ती तरीके से काम करता है: delपायथन के दृष्टिकोण से स्मृति को मुक्त करता है, लेकिन आमतौर पर सी रनटाइम लाइब्रेरी या ओएस के दृष्टिकोण से नहीं। संदर्भ: stackoverflow.com/a/32167625/4297 , effbot.org/pyfaq/…
एरिक ओ लेबिगोट

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

11

मुझे एक फ़ाइल से एक ग्राफ पढ़ने में इसी तरह की समस्या थी। प्रसंस्करण में 200 000x200 000 फ्लोट मैट्रिक्स (एक समय में एक पंक्ति) की गणना शामिल थी जो स्मृति में फिट नहीं थी। gc.collect()समस्या के मेमोरी-संबंधित पहलू का उपयोग करके संगणनाओं के बीच मेमोरी को मुक्त करने की कोशिश की जा रही है, लेकिन इससे प्रदर्शन संबंधी समस्याएं उत्पन्न हुई हैं: मुझे नहीं पता कि क्यों, भले ही उपयोग की गई मेमोरी स्थिर रहे, प्रत्येक नए कॉल में gc.collect()कुछ अधिक समय लगता है पिछला वाला। इसलिए बहुत जल्दी कचरा इकट्ठा करने के लिए गणना का अधिकांश समय लग गया।

स्मृति और प्रदर्शन दोनों समस्याओं को ठीक करने के लिए, मैंने एक बार कहीं पढ़ी गई मल्टीथ्रेडिंग ट्रिक के उपयोग पर स्विच किया (मुझे क्षमा करें, मैं संबंधित पोस्ट को अब और नहीं ढूँढ सकता)। इससे पहले कि मैं फ़ाइल की प्रत्येक पंक्ति को एक बड़े forलूप में पढ़ रहा था , इसे प्रोसेस कर रहा था, और gc.collect()हर एक बार और थोड़ी देर के लिए मेमोरी स्पेस खाली कर रहा था। अब मैं एक फ़ंक्शन को कॉल करता हूं जो एक नए थ्रेड में फ़ाइल का एक हिस्सा पढ़ता है और संसाधित करता है। एक बार धागा समाप्त होने पर, मेमोरी को अजीब प्रदर्शन के मुद्दे के बिना स्वचालित रूप से मुक्त कर दिया जाता है।

व्यावहारिक रूप से यह इस तरह काम करता है:

from dask import delayed  # this module wraps the multithreading
def f(storage, index, chunk_size):  # the processing function
    # read the chunk of size chunk_size starting at index in the file
    # process it using data in storage if needed
    # append data needed for further computations  to storage 
    return storage

partial_result = delayed([])  # put into the delayed() the constructor for your data structure
# I personally use "delayed(nx.Graph())" since I am creating a networkx Graph
chunk_size = 100  # ideally you want this as big as possible while still enabling the computations to fit in memory
for index in range(0, len(file), chunk_size):
    # we indicates to dask that we will want to apply f to the parameters partial_result, index, chunk_size
    partial_result = delayed(f)(partial_result, index, chunk_size)

    # no computations are done yet !
    # dask will spawn a thread to run f(partial_result, index, chunk_size) once we call partial_result.compute()
    # passing the previous "partial_result" variable in the parameters assures a chunk will only be processed after the previous one is done
    # it also allows you to use the results of the processing of the previous chunks in the file if needed

# this launches all the computations
result = partial_result.compute()

# one thread is spawned for each "delayed" one at a time to compute its result
# dask then closes the tread, which solves the memory freeing issue
# the strange performance issue with gc.collect() is also avoided

1
मुझे आश्चर्य है कि आप टिप्पणियों के लिए पायथन में # के बजाय `//` का उपयोग क्यों कर रहे हैं।
जेसी रोकोमांडे

मैं भाषाओं के बीच घुलमिल गया। टिप्पणी के लिए धन्यवाद, मैंने वाक्यविन्यास को अद्यतन किया।
रेट्रोज़ोड

9

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

वास्तव में पाइथन को मुफ्त मेमोरी बताने का कोई तरीका नहीं है। इस मामले का तथ्य यह है कि यदि आप चाहते हैं कि नियंत्रण का स्तर कम हो, तो आपको C या C ++ में एक्सटेंशन लिखना होगा।

उस ने कहा, इस के साथ मदद करने के लिए कुछ उपकरण हैं:


3
gc.collect () और डेल gorgarbage [:] ठीक काम करते हैं जब मैं बड़ी मात्रा में मेमोरी का उपयोग कर रहा हूं
एंड्रयू स्कॉट इवांस

3

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


1
मुझे लगता है कि मैं केवल कोने को स्मृति में रख सकता हूं और त्रिकोणों को एक फ़ाइल में प्रिंट कर सकता हूं, और फिर अंत में केवल कोने को प्रिंट कर सकता हूं। हालाँकि, किसी फ़ाइल में त्रिकोण लिखने का कार्य एक बहुत बड़ा प्रदर्शन है। वहाँ लाने के लिए कोई तरीका है कि अप?
नाथन फ़ेलमैन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.