经过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)。
答案 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
,并且您的用户将获得更快速的响应!