簡體   English   中英

Python:垃圾收集失敗了嗎?

[英]Python: garbage collection fails?

請考慮以下腳本:

l = [i for i in range(int(1e8))]
l = []
import gc
gc.collect()
# 0
gc.get_referrers(l)
# [{'__builtins__': <module '__builtin__' (built-in)>, 'l': [], '__package__': None, 'i': 99999999, 'gc': <module 'gc' (built-in)>, '__name__': '__main__', '__doc__': None}]
del l
gc.collect()
# 0

關鍵是,在所有這些步驟之后,我的機器上的這個python進程的內存使用率大約是30%(Python 2.6.5,請求的更多細節?)。 這是top的輸出的摘錄:

 PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND  
5478 moooeeeep 20   0 2397m 2.3g 3428 S    0 29.8   0:09.15 ipython  

RESP。 ps aux

moooeeeep 5478  1.0 29.7 2454720 2413516 pts/2 S+   12:39   0:09 /usr/bin/python /usr/bin/ipython gctest.py

根據gc.collect 的文檔

由於特定的實現,並非某些空閑列表中的所有項都可能被釋放,特別是intfloat

這是否意味着,如果我(暫時)需要大量不同的intfloat數字,我需要將其導出到C / C ++,因為Python GC無法釋放內存?


更新

正如本文所暗示的那樣,解釋器可能是罪魁禍首:

這是你同時創建了500萬個整數,每個int對象消耗12個字節。 “為了速度”,Python維護整數對象的內部空閑列表。 不幸的是,這個免費清單既是不朽的,也是無限的。 花車也使用不朽的無限列表。

然而問題仍然存在,因為我無法避免這些數據(來自外部源的時間戳/值對)。 我真的被迫放棄Python並回到C / C ++嗎?


更新2

可能確實是這樣,Python實現會導致問題。 找到這個答案最終解釋了問題和可能的解決方法。

你的答案可能在這里

Python做了很多分配和解除分配。 所有對象(包括整數和浮點數等“簡單”類型)都存儲在堆上。 為每個變量調用malloc和free會非常慢。 因此,Python解釋器使用各種優化的內存分配方案。 最重要的一個是名為pymalloc的malloc實現,專門用於處理大量的小分配。 任何小於256字節的對象都使用此分配器,而更大的任何對象使用系統的malloc。 此實現永遠不會將內存返回給操作系統。 相反,它會保留它,以防再次需要它 這在短時間內再次使用時是有效的,但如果在需要之前經過很長時間就會浪費。

我做了一些測試,這個問題只發生在CPython 2.x. 這個問題在CPython 3.2.2中消失了(它回到了新解釋器的內存使用情況),而PyPy 1.8(python 2.7.2)也降低到了與新的pypy進程相同的水平。

所以不,你不需要切換到另一種語言。 但是,可能有一種解決方案不會強制您切換到不同的Python實現。

發現這也是Alex Martelli在另一個帖子中回答的問題

不幸的是(取決於你的版本和Python版本),某些類型的對象使用“自由列表”,這是一個簡潔的局部優化但可能導致內存碎片,特別是通過為特定類型的對象創建更多的內存“專用”和從而無法獲得“普通基金”。

確保大量但臨時使用內存的唯一真正可靠的方法是在完成后將所有資源返回給系統,就是在子進程中使用該進程,這會占用大量內存,然后終止工作。 在這種情況下,操作系統將完成其工作,並樂意回收子進程可能已經吞噬的所有資源。 幸運的是,多處理模塊在現代版本的Python中進行這種操作(過去相當痛苦)並不算太糟糕。

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

幸運的是,我能夠將內存密集型工作拆分為單獨的塊,這使得解釋器在每次迭代后實際釋放臨時內存。 我使用以下包裝器作為子進程運行內存密集型函數:

import multiprocessing

def run_as_process(func, *args):
    p = multiprocessing.Process(target=func, args=args)
    try:
        p.start()
        p.join()
    finally:
        p.terminate()

Python傾向於相當智能地進行垃圾收集,並且根據我的經驗釋放內存就好了。 考慮到它確實有一個小的開銷(我的大約15Mb),但除此之外,內存要求與C沒有什么不同。如果你處理的是如此多的數據,那么內存是一個嚴重的問題,你可能會去在C中遇到同樣的問題,因此嘗試更改數據處理方式會好得多,例如將其存儲在頁面文件中並一次使用一個可管理的卡盤。

暫無
暫無

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

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