首页 文章

我怎样才能在Python中明确释放内存?

提问于
浏览
253

我写了一个Python程序,它作用于一个大的输入文件,创建了几百万个表示三角形的对象 . 算法是:

  • 读取输入文件

  • 处理文件并创建由其顶点表示的三角形列表

  • 以OFF格式输出顶点:顶点列表,后跟三角形列表 . 三角形由顶点列表中的索引表示

在打印三角形之前打印出完整的顶点列表的OFF的要求意味着在将输出写入文件之前我必须在内存中保存三角形列表 . 与此同时,由于列表的大小,我遇到了内存错误 .

告诉Python我不再需要某些数据的最佳方法是什么,它可以被释放?

9 回答

  • 18

    根据Python Official Documentation,您可以强制垃圾收集器使用 gc.collect() 释放未引用的内存 . 例:

    import gc
    gc.collect()
    
  • 22

    不幸的是(取决于你的版本和Python版本),某些类型的对象使用“自由列表”,这是一个简洁的局部优化但可能导致内存碎片,特别是通过为特定类型的对象创建越来越多的内存“专用”从而无法获得“普通基金” .

    确保大量但临时使用内存的唯一真正可靠的方法是在完成后将所有资源返回给系统,就是在子进程中使用该进程,这会占用大量内存,然后终止工作 . 在这种情况下,操作系统将完成其工作,并乐意回收子进程可能已经吞噬的所有资源 . 幸运的是, multiprocessing 模块在现代版本的Python中进行这种操作(过去相当痛苦)并不算太糟糕 .

    在您的用例中,似乎子进程积累一些结果并确保主进程可用的结果的最佳方法是使用半临时文件(半临时文件,我的意思是,不是那种文件,关闭时会自动消失,只有当你完成它们时才明确删除的普通文件) .

  • 271

    del 声明可能有用,但是IIRC it isn't guaranteed to free the memory . docs are here ......和why it isn't released is here .

    我听说有人在Linux和Unix类型的系统上分支python进程做一些工作,得到结果然后杀死它 .

    This article有关于Python垃圾收集器的注释,但我认为 lack of memory control is the downside to managed memory

  • 7

    Python是垃圾收集的,因此如果减小列表的大小,它将回收内存 . 您还可以使用“del”语句完全删除变量:

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

    你无法明确释放内存 . 您需要做的是确保不保留对象的引用 . 然后他们将被垃圾收集,释放内存 .

    在您的情况下,当您需要大型列表时,通常需要重新组织代码,通常使用生成器/迭代器 . 这样你根本不需要在内存中有大的列表 .

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

  • 92

    del 可以是你的朋友,因为它在没有其他引用的情况下将对象标记为可删除 . 现在,CPython解释器通常保留此内存供以后使用,因此您的操作系统可能看不到"freed"内存 . )

    也许你不会通过为数据使用更紧凑的结构来解决任何内存问题 . 因此,数字列表的内存效率远低于标准 array 模块或第三方 numpy 模块使用的格式 . 您可以通过将顶点放在NumPy 3xN数组中并将三角形放在N元素数组中来节省内存 .

  • 15

    其他人已经发布了一些方法,您可以“哄骗”Python解释器释放内存(或以其他方式避免出现内存问题) . 你有可能首先尝试他们的想法 . 但是,我觉得重要的是直接回答你的问题 .

    没有任何方法可以直接告诉Python释放内存 . 事实上,如果你想要低水平的控制,你将不得不用C或C编写扩展 .

    也就是说,有一些工具可以帮助解决这个问题:

  • 3

    我在从文件中读取图表时遇到了类似的问题 . 处理包括计算不适合存储器的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
    
  • 6

    如果你不关心顶点重用,你可以有两个输出文件 - 一个用于顶点,一个用于三角形 . 完成后,将三角形文件附加到顶点文件 .

相关问题