Django Python垃圾收集困境

时间:2011-01-04 14:12:03

标签: python django garbage-collection

经过2天的调试,我确定了我的时间:Python垃圾收集器 我的应用程序在内存中保存了很多对象。它运作良好。
GC执行常规轮次(我没有使用默认阈值(700,10,10)) 偶尔,在一次重要的交易过程中,第二代扫描开始并检查我的~1.5M第2代物体。
这需要2秒! 名义交易时间不到0.1秒。

我的问题是我该怎么办? 我可以关闭第2代扫描(通过设置一个非常高的阈值 - 这是正确的方法吗?)并且GC是顺从的。
我该什么时候打开它们? 我们使用Django实现了一个Web服务,每个用户请求大约需要0.1秒 最理想的情况是,我将在用户API请求之间运行这些GC gen 2周期。但是我该怎么做呢? 我的观点以return HttpResponse() AFTER 结束,我希望运行第2代GC扫描。
我怎么做?这种方法是否有意义?

我是否可以标记不需要进行垃圾收集的对象,以便GC不会在每个第二代循环中对它们进行测试? 当Django服务器相对空闲时,如何配置GC运行完全扫描?

多平台上的Python 2.6.6(Windows / Linux)。

5 个答案:

答案 0 :(得分:3)

我相信一个选项是完全禁用垃圾收集,然后在请求结束时手动收集,如下所示:How does the Garbage Collection mechanism work?

我想您可以在settings.py文件中禁用GC。

如果你想在每个请求上运行GarbageCollection,我建议开发一些用process response方法完成它的中间件:

import gc
class GCMiddleware(object):
    def process_response(self, request, response):
        gc.collect()
        return response

答案 1 :(得分:1)

另一种方法可能是完全禁用GC,并配置mod_wsgi(或任何你正在使用的)来更频繁地终止和重启进程。

答案 2 :(得分:1)

我们为gunicorn做过类似的事情。根据您使用的wsgi服务器,您需要在响应之后找到正确的挂钩,而不是之前。 Django有一个request_finished信号,但该信号仍然是预响应。

对于gunicorn,在配置中你需要定义两个方法,如:

def pre_request(worker, req):
    # disable gc until end of request
    gc.disable()


def post_request(worker, req, environ, resp):
    # enable gc after a request
    gc.enable()

post_request此处在http响应发布后运行,因此是垃圾收集的最佳时机。

答案 3 :(得分:0)

  

我的观点以返回HttpResponse()结束,之后我想进行第2代GC扫描。

// turn off GC
// do stuff
resp = HttpResponse()
// turn on GC
return resp

我不确定,但您可以//turn on GC而不是// spawn thread to turn on GC in 0.1 sec

为了确保在处理请求之后GC不会发生,如果线程产生不起作用,则需要修改django本身或使用某种django钩子,如dcurtis建议的那样。 / p>

如果您正在处理性能关键代码,您可能还需要考虑为该部分使用手动内存管理语言(如C / C ++),并使用Python简单地调用/查询它。

答案 4 :(得分:0)

基于@milkypostman 的方法,您可以使用 gevent。您希望每个请求都调用一次垃圾收集,但是@milkypostman 建议的问题是对 gc.collect() 的调用仍会阻止请求的返回。 Gevent 让我们立即返回并让 GC 运行进程返回后继续。

首先在您的 wsgi 文件中,确保使用 gevent 魔术材料修补所有内容并禁用垃圾收集。您可以设置 gc.disable(),但某些库的上下文管理器会在禁用它后将其打开(例如 messagepack),因此 0 阈值更具粘性。

import gc
from gevent import monkey

# Disable garbage collection runs
gc.set_threshold(0)
# Apply gevent monkey magic
monkey.patch_all()

然后像这样为 Django 创建一些中间件:

from gc import collect
import gevent

class BaseMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response


class GcCollectMiddleware(BaseMiddleware):
    """Middleware which performs a non-blocking gc.collect()"""

    def __call__(self, request):
        response = self.get_response(request)
        gevent.spawn(collect)
        return response

您将在这里看到与之前建议的方法的主要区别在于 gc.collect() 包含在 gevent.spawn 中,这不会阻止返回 HttpResponse,并且您的用户将获得更快速的响应!