Python 为什么';django中的大型查询(或一系列查询)之后,内存是否会释放到系统中?

Python 为什么';django中的大型查询(或一系列查询)之后,内存是否会释放到系统中?,python,django,memory-leaks,Python,Django,Memory Leaks,首先,在settings.py中,DEBUG=False,因此没有,connections['default']。查询在耗尽所有内存之前不会不断增长 让我们从这样一个事实开始:我已经从django.contrib.auth.models.User加载了User表,其中有10000个用户(每个名为“test#”,其中#是一个介于1和10000之间的数字) 以下是视图: from django.contrib.auth.models import User from django.http impo

首先,在settings.py中,
DEBUG=False
,因此没有,
connections['default']。查询在耗尽所有内存之前不会不断增长

让我们从这样一个事实开始:我已经从
django.contrib.auth.models.User
加载了
User
表,其中有10000个用户(每个名为“test#”,其中#是一个介于1和10000之间的数字)

以下是视图:

from django.contrib.auth.models import User
from django.http import HttpResponse

import time

def leak(request):
    print "loading users"

    users = []
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())
    users += list(User.objects.all())

    print "sleeping"
    time.sleep(10)

    return HttpResponse('')
我已经将上面的视图附加到
/leak/
url并启动开发服务器(DEBUG=False,我已经测试过了,它与运行开发服务器与其他实例无关)

运行后:

% curl http://localhost:8000/leak/
runserver进程的内存增长到下面输出的大小,然后保持在该级别

USER       PID %CPU %MEM    VSZ    RSS TTY      STAT START   TIME COMMAND
dlamotte 25694 11.5 34.8 861384 705668 pts/3    Sl+  19:11   2:52 /home/dlamotte/tmp/django-mem-leak/env/bin/python ./manage.py runserver
那么,运行上面的
curl
命令似乎不会增加实例的内存使用量(我从真正的内存泄漏中预期的情况?),它一定是在重新使用内存?但是,我觉得这里有一些错误,内存没有释放到系统中(但是,我理解python不释放内存可能会提高性能)

接下来,我天真地试图看看python是否会释放它分配的大块内存。因此,我尝试在python会话中执行以下操作:

>>> a = ''
>>> a += 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' * 10000000
>>> del a
内存按预期分配在
a+=…
行上,但当
dela
发生时,内存被释放。为什么django查询集的行为不同?这是django打算做的事情吗?有没有办法改变这种行为

我花了2天时间调试这个行为,但不知道下一步该怎么做(我学会了使用guppy和objgraph,它们似乎没有指向任何我能找到的有趣的东西)

更新:这可能只是工作中的python内存管理,与Django无关(在Django用户邮件列表中建议),但我希望通过某种方式在Django之外的python中复制来确认这一点


更新:使用python 2.6.5版

许多应用程序、语言运行时,甚至一些系统内存分配器将尽可能长时间地保留释放的内存,以便重新使用,纯粹出于性能目的。在像Django这样的复杂系统中,它可以是任意数量的扩展,可能是用C实现的,表现出这种行为,也可能是带有某种内存池的Python,或者是惰性垃圾收集

它甚至可能是底层的malloc实现,或者您的操作系统为您的进程保留一定数量的内存空间,即使进程没有显式地使用它。不过,不要引用我的话——我已经有一段时间没有研究过这些事情了


但总的来说,如果在初始alloc和dealloc之后重复分配过程不会使所用内存量增加一倍,那么您看到的不是内存泄漏,而是内存池。只有当你的机器上有很多进程在争夺有限的内存时,这才有可能成为一个问题。

我决定把我的评论变成一个答案,让事情更清楚

自Python 2.5以来,CPython内存分配跟踪小对象分配器的内部内存使用情况,并尝试将完全免费的Arena返回到底层操作系统。这在大多数情况下都是有效的,但是对象不能在内存中移动这一事实意味着碎片可能是一个严重的问题

尝试以下实验(我使用了3.2,但如果使用xrange,则2.5+应该类似):

注意,即使删除对
seq1
seq2
的引用,上述序列也可能会让Python进程保留大量额外内存


当人们谈论PyPy使用的内存比CPython少时,这是他们谈论的主要部分。由于PyPy不在后台使用直接指针引用,因此它能够使用压缩GC,从而避免了大量碎片问题,并更可靠地将内存返回操作系统。

+1。我对这个问题的答案很感兴趣。我还发现Django在一段时间内在生产过程中出现了奇怪的内存泄漏(DEBUG=False,甚至对于非常简单的应用程序/项目也是如此)。如果您
del users
,会发生什么情况?您使用的Python版本是什么(用于服务器和命令行测试)?旧版本不会将为对象分配的内存释放回系统,因此如果本地版本为2.5或更高版本,但服务器运行的是2.4,则可能是您的问题。单个大的分配(比如你的大字符串)也可能绕过分配器-看看像
([[]]*10**6)
这样的东西会发生什么。对于很多小对象,你也会看到内存碎片问题,所以即使是最近的分配器也无法释放出完整的块返回操作系统。作为一个实验,使用我之前评论中的大列表,每10个对象抓取一个引用
x=seq[::10]
左右,然后删除原始列表,看看你给操作系统的内存有多少(可能很少)。我在上述评论中的示例不正确-我发布了一个更正的版本作为答案。Dropbox团队在Pycon演讲中对这个问题做了非常好的描述(在18:00的幻灯片中首次提到,在26:00左右更详细):您可以链接到源代码的相关部分吗?对象分配器的开发版本:将URL中的“默认”段更改为相关的Python版本,以获取该分支上分配器代码的最新版本。回答很好,只是对大量神奇的“挥手”解释不感兴趣。。。我喜欢ncoghlan给我举了一个如何重复这个问题的例子,谢谢你的输入,很遗憾,一个bi
# Create the big lists in advance to avoid skewing the memory counts
seq1 = [None] * 10**6 # Big list of references to None
seq2 = seq1[::10]

# Create and reference a lot of smaller lists
seq1[:] = [[] for x in range(10**6)] # References all the new lists
seq2[:] = seq1[::10] # Grab a second reference to 10% of the new lists

# Memory fragmentation in action
seq1[:] = [None] * 10**6 # 90% of the lists are no longer referenced here
seq2[:] = seq1[::10] # But memory freed only after last 10% are dropped