我如何使用子进程强制python释放内存?

时间:2014-05-29 15:38:05

标签: python python-2.7 memory-management subprocess

我正在阅读Python Memory Management并希望减少应用程序的内存占用。 It was suggested subprocesses {{}}}可以在很大程度上缓解问题;但我无法概念化需要做什么。有人可以提供一个如何解决这个问题的简单例子......

def my_function():
    x = range(1000000)
    y = copy.deepcopy(x)
    del x
    return y

@subprocess_witchcraft
def my_function_dispatcher(*args):
    return my_function()

...进入真正的子处理函数,该函数不存储额外的“免费列表”?

奖金问题:

这个“自由列表”概念是否也适用于python c-extensions?

1 个答案:

答案 0 :(得分:29)

优化建议的重要一点是确保仅在子进程中调用my_function()deepcopydel是无关紧要的 - 一旦你在一个过程中创建了五百万个不同的整数,同时保持所有这些整数,它就会结束游戏。即使你不再引用那些对象,Python也会通过在一个边界中保留对500万个空整数对象大小的字段的引用来释放它们,它们等待重用于下一个想要创建500万个整数的函数。这是另一个答案中提到的免费列表,它可以快速分配和释放整数和浮点数。对于Python来说,注意这不是内存泄漏是公平的,因为内存肯定可用于进一步分配。但是,在进程结束之前,该内存不会返回到系统,除了分配相同类型的数字之外,它也不会被重用。

大多数程序都没有这个问题,因为大多数程序都没有创建病态庞大的数字列表,释放它们,然后希望将这些内存重用于其他对象。使用numpy的程序也是安全的,因为numpy以紧密打包的本机格式存储其数组的数字数据。对于遵循此使用模式的程序,缓解问题的方法是首先不要同时创建大量的整数,至少不要在需要将内存返回给系统的过程中。目前还不清楚你有什么确切的用例,但现实世界的解决方案可能需要的不仅仅是一个魔术装饰者"。

这是子进程的来源:如果数字列表是在另一个进程中创建的,那么与该列表关联的所有内存(包括但不限于int的存储)都将被释放并仅通过纯粹的方式返回给系统终止子流程的行为。当然,您必须设计程序,以便可以在子系统中创建和处理列表,而无需传输所有这些数字。子进程可以接收创建数据集所需的信息,并可以发回从处理列表中获得的信息。

为了说明原理,让我们升级您的示例,以便实际需要存在整个列表 - 比如我们对排序算法进行基准测试。我们想要创建一个庞大的整数列表,对它进行排序,并可靠地释放与列表相关的内存,以便下一个基准测试可以根据自己的需要分配内存,而不必担心RAM耗尽。要生成子流程并进行通信,这将使用multiprocessing模块:

# To run this, save it to a file that looks like a valid Python module, e.g.
# "foo.py" - multiprocessing requires being able to import the main module.
# Then run it with "python foo.py".

import multiprocessing, random, sys, os, time

def create_list(size):
    # utility function for clarity - runs in subprocess
    maxint = sys.maxint
    randrange = random.randrange
    return [randrange(maxint) for i in xrange(size)]

def run_test(state):
    # this function is run in a separate process
    size = state['list_size']
    print 'creating a list with %d random elements - this can take a while... ' % size,
    sys.stdout.flush()
    lst = create_list(size)
    print 'done'
    t0 = time.time()
    lst.sort()
    t1 = time.time()
    state['time'] = t1 - t0

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    state = manager.dict(list_size=5*1000*1000)  # shared state
    p = multiprocessing.Process(target=run_test, args=(state,))
    p.start()
    p.join()
    print 'time to sort: %.3f' % state['time']
    print 'my PID is %d, sleeping for a minute...' % os.getpid()
    time.sleep(60)
    # at this point you can inspect the running process to see that it
    # does not consume excess memory

奖金回答

很难提供奖金问题的答案,因为问题不清楚。 "免费列表概念"正是如此,一个概念,一个需要在常规Python分配器之上显式编码的实现策略。大多数Python类型使用该分配策略,例如,它不用于使用class语句创建的类的实例。实施免费清单并不难,但它相当先进,很少在没有充分理由的情况下进行。如果某个扩展作者选择使用其中一种类型的免费列表,则可以预期他们知道免费列表提供的权衡 - 获得额外的快速分配/解除分配的代价一些额外的空间(对于空闲列表中的对象和自由列表本身)以及无法将内存重用于其他内容。