如何在Python中显式释放内存?

如何在Python中显式释放内存?,python,memory,memory-management,Python,Memory,Memory Management,我编写了一个Python程序,它作用于一个大的输入文件来创建几百万个表示三角形的对象。算法是: 读取输入文件 处理该文件并创建三角形列表,由其顶点表示 以关闭格式输出顶点:顶点列表,后跟三角形列表。三角形由顶点列表中的索引表示 关闭的要求是在打印三角形之前打印出完整的顶点列表,这意味着在将输出写入文件之前,必须将三角形列表保存在内存中。同时,由于列表的大小,我得到了内存错误 告诉Python我不再需要某些数据,并且可以将其释放的最佳方法是什么?Python是垃圾收集的,因此如果减少列表的大小,它

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

  • 读取输入文件
  • 处理该文件并创建三角形列表,由其顶点表示
  • 以关闭格式输出顶点:顶点列表,后跟三角形列表。三角形由顶点列表中的索引表示
  • 关闭的要求是在打印三角形之前打印出完整的顶点列表,这意味着在将输出写入文件之前,必须将三角形列表保存在内存中。同时,由于列表的大小,我得到了内存错误


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

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

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

    del
    语句可能有用,但IIRC不能保证释放内存。这个还有一个

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


    有关于Python垃圾收集器的说明,但我认为缺少内存控制是托管内存的缺点

    您无法显式释放内存。您需要做的是确保不保留对对象的引用。然后它们将被垃圾收集,释放内存

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

    根据,您可以使用
    gc.collect()
    显式调用垃圾收集器来释放未引用的内存。例如:

    import gc
    
    gc.collect()
    
    您应该在使用
    del
    标记要放弃的内容后执行此操作:

    del my_array
    del my_object
    gc.collect()
    
    不幸的是(取决于您的Python版本和发行版),某些类型的对象使用“空闲列表”,这是一种整洁的本地优化,但可能会导致内存碎片,特别是通过将越来越多的内存“专用”于某一类型的对象,从而使“普通基金”无法使用

    唯一真正可靠的方法是在子进程中使用内存,这样可以确保大量但临时的内存使用在完成时将所有资源返回给系统,而子进程完成了内存不足的工作,然后终止。在这种情况下,操作系统将完成其工作,并乐意回收子流程可能占用的所有资源。幸运的是,
    多处理
    模块使这种操作(过去相当痛苦)在Python的现代版本中不太糟糕


    在您的用例中,似乎子流程积累一些结果并确保这些结果对主流程可用的最佳方法是使用半临时文件(我所说的半临时文件不是指关闭时自动消失的文件,而是指在处理完所有文件后明确删除的普通文件).

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

    其他人发布了一些方法,您可以“哄骗”Python解释器释放内存(或者避免内存问题)1.你应该先尝试一下他们的想法。不过,我觉得直接回答你的问题很重要

    <> P>没有任何方法直接告诉Python来释放内存。事实上,如果你想要低水平的控制,你就必须在C或C++中写一个扩展。 也就是说,有一些工具可以帮助做到这一点:

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

    通过使用更紧凑的数据结构,您可能不会首先遇到任何内存问题。
    因此,与标准的
    数组
    模块或第三方
    numpy
    模块使用的格式相比,数字列表的内存效率要低得多。将顶点放在一个numpy 3xN数组中,将三角形放在一个N元素数组中,可以节省内存。

    我在从文件中读取图形时遇到了类似的问题g包括计算不适合内存的20000x200 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代码不再使用它(因此
    gc.collect()<
    
    import ctypes
    libc = ctypes.CDLL("libc.so.6")
    libc.malloc_trim(0)