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

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

  1. 读取输入文件
  2. 处理该文件并创建一个三角形列表,由它们的顶点表示
  3. 以OFF格式输出顶点:顶点列表后面跟着三角形列表。三角形由顶点列表中的索引表示

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

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

735591 次浏览

Python是垃圾回收的,所以如果你减少列表的大小,它会回收内存。你也可以使用"del"语句完全摆脱一个变量:

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

del语句可能有用,但是IIRC 它不能保证释放内存文档在这里…和为什么没有在这里发布

我听说有人在Linux和unix类型的系统上使用python进程来做一些工作,得到结果,然后终止它。

这篇文章有关于Python垃圾回收器的注释,但我认为缺少内存控制是托管内存的缺点

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

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

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

import gc


gc.collect()

你应该在标记你想要丢弃的东西后使用del:

del my_array
del my_object
gc.collect()

不幸的是(取决于你的Python版本和发行版),某些类型的对象使用“自由列表”,这是一种简洁的局部优化,但可能会导致内存碎片,特别是通过将越来越多的内存“指定”给特定类型的对象,从而无法用于“普通基金”。

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

在您的用例中,似乎子进程积累一些结果并确保这些结果对主进程可用的最佳方法是使用半临时文件(我所说的半临时是指,不是那种关闭时自动消失的文件,而是在您全部使用完后显式删除的普通文件)。

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

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

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

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

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

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

我在从文件中读取图表时遇到了类似的问题。处理包括计算一个不适合内存的20000x200000浮点矩阵(一次一行)。尝试在计算之间使用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()不会释放任何东西),特别是在一个长时间运行的程序中。无论如何,如果你在Linux上,你可以尝试通过直接调用libc函数malloc_trim (手册页)来释放内存。 比如:< / p >
import ctypes
libc = ctypes.CDLL("libc.so.6")
libc.malloc_trim(0)