簡體   English   中英

Python-從文件讀取時發生奇怪的內存泄漏

[英]Python - Strange Memory Leak While Reading From File

我遇到一個奇怪的問題,在Python2中從文件讀取時發生奇怪的內存泄漏。

我試圖消除泄漏的代碼達幾個小時,但失敗了。 然后,我試圖隔離泄漏的代碼,並編寫了一個最小的程序來再現相同的泄漏。 這里是:

import sys
import re

@profile
def leak(filename):
    residual_line = []  
    with open(filename) as f:
        for line in f:
            splitted_line = re.split(r' |\n', line)
            del line
            filtered_line = filter(lambda x: x != '', splitted_line)
            del splitted_line
            filtered_line = residual_line + filtered_line
            for x in range(0,len(filtered_line)):
                a=5
            residual_line = filtered_line
            del filtered_line
    del residual_line

@profile
def main():
    filename = sys.argv[1]
    leak(filename)
    sys.exit(0)

main()

我正在使用memory_profiler模塊對其進行性能分析,這是性能分析的輸出:

在此處輸入圖片說明

如您所見,內存分配發生在第8行,但從未釋放。 泄漏為31 KiB,我嘗試讀取的文件為3.4kB。 如果我將文件大小加倍,泄漏將變為70KiB,再將160KiB加倍,因此泄漏可能取決於文件。

我希望有人能找到泄漏,在此先感謝。

我懷疑是否存在內存泄漏,我只是覺得您的方法來自對Python內存分配工作原理的缺乏了解。

內存分配簡介

內存分配在Python中有多個層次。 有系統自己的分配器,當您使用Windows任務管理器或ps檢查內存使用情況時,就會顯示該分配器。 然后是C運行時的內存分配器(malloc),它從系統分配器獲取內存,並將其以較小的塊分發給應用程序。 最后,還有Python自己的對象分配器,該對象分配器用於最多256個字節的對象。 該分配器從C分配器獲取大塊內存,並使用針對Python精心調整的算法將它們切成小塊。

這是一個演示此示例的示例:首先創建一個對象,將其刪除,然后收集剩余的變量:

import gc

@profile
def create_list():
    lst = list(range(1000000))
    del lst
    gc.collect()


if __name__ == '__main__':
    create_list()

結果如下:

└──> python -m memory_profiler test.py
Filename: test.py

Line #    Mem usage    Increment   Line Contents
================================================
     3   20.414 MiB    0.000 MiB   @profile
     4                             def create_list():
     5   51.461 MiB   31.047 MiB       lst = list(range(1000000))
     6   43.828 MiB   -7.633 MiB       del lst
     7   26.793 MiB  -17.035 MiB       gc.collect()

如果您注意到,大多數內存將永遠不會返回到系統,而是保留在Python分配器或C分配器中。

如果我對文件執行相同的操作,則會得到相同的結果:

import gc

@profile
def iter_file():
    with open('/path/to/somefile') as f:
        for line in f:
            del line
            gc.collect()


if __name__ == '__main__':
    iter_file()

結果如下:

└──> python -m memory_profiler test.py
Filename: test.py

Line #    Mem usage    Increment   Line Contents
================================================
     3   20.496 MiB    0.000 MiB   @profile
     4                             def iter_file():
     5   20.496 MiB    0.000 MiB       with open('/path/to/somefile') as f:
     6   20.801 MiB    0.305 MiB           for line in f:
     7   20.613 MiB   -0.188 MiB               del line
     8   20.613 MiB    0.000 MiB               gc.collect()

簡而言之,不必擔心內存泄漏:只要確保變量超出范圍,Python就會為您處理。

這也意味着您不必刪除每個新創建的變量:當重新定義該變量,超出范圍時,將自動對其進行垃圾回收,並將內存返回給分配器,甚至還可能返回給系統。

暫無
暫無

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

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