簡體   English   中英

如何在 Python 中顯式釋放內存?

[英]How can I explicitly free memory in Python?

我編寫了一個 Python 程序,該程序作用於一個大型輸入文件,以創建代表三角形的幾百萬個對象。 算法是:

  1. 讀取輸入文件
  2. 處理文件並創建一個三角形列表,由它們的頂點表示
  3. 以 OFF 格式輸出頂點:頂點列表后跟三角形列表。 三角形由頂點列表中的索引表示

在打印出三角形之前打印出完整的頂點列表的 OFF 要求意味着我必須在將輸出寫入文件之前將三角形列表保存在內存中。 與此同時,由於列表的大小,我收到了內存錯誤。

告訴 Python 我不再需要某些數據並且可以將其釋放的最佳方法是什么?

根據Python 官方文檔,您可以使用gc.collect()顯式調用垃圾收集器來釋放未引用的內存。 例子:

import gc

gc.collect()

在使用del標記要丟棄的內容后,您應該這樣做:

del my_array
del my_object
gc.collect()

不幸的是(取決於您的 Python 版本和發行版)某些類型的對象使用“空閑列表”,這是一種整潔的本地優化,但可能會導致內存碎片,特別是通過將越來越多的內存“指定”給特定類型的對象和因此無法用於"普通基金"。

確保大量但臨時的內存使用在完成后將所有資源返回給系統的唯一真正可靠的方法是讓該使用發生在子進程中,該子進程會執行需要內存的工作然后終止。 在這種情況下,操作系統將完成它的工作,並很樂意回收子進程可能吞噬的所有資源。 幸運的是, multiprocessing模塊使這種操作(這在過去很痛苦)在現代版本的 Python 中還算不錯。

在您的用例中,子流程積累一些結果並確保這些結果可用於主流程的最佳方法似乎是使用半臨時文件(半臨時我的意思是,不是那種文件關閉時自動消失,只是在完成所有操作后明確刪除的普通文件)。

del語句可能有用,但 IIRC不能保證釋放內存 文檔在這里......以及為什么它沒有被發布在這里

我聽說有人在 Linux 和 Unix 類型的系統上分叉一個 python 進程來做一些工作,得到結果然后殺死它。

這篇文章有關於 Python 垃圾收集器的注釋,但我認為缺乏內存控制是托管內存的缺點

Python 是垃圾收集的,因此如果您減小列表的大小,它將回收內存。 您還可以使用“del”語句完全擺脫變量:

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

del可以成為您的朋友,因為當沒有其他引用對象時,它會將對象標記為可刪除。現在,CPython 解釋器通常會保留此內存以備后用,因此您的操作系統可能看不到“釋放”的內存。)

通過為數據使用更緊湊的結構,您可能一開始就不會遇到任何內存問題。 因此,與標准array模塊或第三方numpy模塊使用的格式相比,數字列表的內存效率要低得多。 您可以通過將頂點放在 NumPy 3xN 數組中並將三角形放在 N 元素數組中來節省內存。

您無法明確釋放內存。 您需要做的是確保不保留對對象的引用。 然后它們將被垃圾收集,釋放內存。

在您的情況下,當您需要大型列表時,您通常需要重新組織代碼,通常使用生成器/迭代器代替。 這樣你根本不需要在內存中擁有大列表。

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

我在從文件中讀取圖形時遇到了類似的問題。 處理包括計算一個無法放入內存的 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

其他人已經發布了一些方法,您可以“哄” Python 解釋器釋放內存(或以其他方式避免出現內存問題)。 很有可能你應該先嘗試他們的想法。 但是,我認為直接回答您的問題很重要。

實際上沒有任何方法可以直接告訴 Python 釋放內存。 這件事的事實是,如果你想要那么低的控制級別,你將不得不用 C 或 C++ 編寫一個擴展。

也就是說,有一些工具可以幫助解決這個問題:

正如其他答案已經說過的那樣,Python 可以避免向操作系統釋放內存,即使它不再被 Python 代碼使用(因此gc.collect()不會釋放任何東西),尤其是在長時間運行的程序中。 無論如何,如果您在 Linux 上,您可以嘗試通過直接調用 libc 函數malloc_trim手冊頁)來釋放內存。 就像是:

import ctypes
libc = ctypes.CDLL("libc.so.6")
libc.malloc_trim(0)

如果你不關心頂點重用,你可以有兩個輸出文件——一個用於頂點,一個用於三角形。 然后在完成后將三角形文件附加到頂點文件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM