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

时间:2009-08-22 19:10:35

标签: python memory memory-management

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

  1. 读取输入文件
  2. 处理文件并创建一个由顶点表示的三角形列表
  3. 以OFF格式输出顶点:顶点列表,后跟三角形列表。三角形由顶点列表中的索引表示
  4. 在我打印三角形之前打印出完整的顶点列表的OFF的要求意味着在将输出写入文件之前我必须在内存中保存三角形列表。与此同时,由于列表的大小,我遇到了内存错误。

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

10 个答案:

答案 0 :(得分:321)

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

import gc
gc.collect()

答案 1 :(得分:97)

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

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

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

答案 2 :(得分:38)

del声明可能有用,但IIRC 无法保证释放内存docs are here ...和why it isn't released is here

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

This article有关于Python垃圾收集器的注释,但我认为内存控制不足是内存管理的缺点

答案 3 :(得分:27)

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

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

答案 4 :(得分:18)

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

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

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

答案 5 :(得分:16)

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

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

答案 6 :(得分:8)

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

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

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

答案 7 :(得分:7)

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

答案 8 :(得分:3)

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

答案 9 :(得分:1)

正如其他答案已经说过的,即使Python代码不再使用Python(因此gc.collect()不会释放任何东西),Python仍可以阻止向操作系统释放内存,尤其是在长时间运行的程序中。无论如何,如果您使用的是Linux,则可以尝试通过直接调用libc函数malloc_trimman page)来释放内存。 像这样:

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