python-pyramid app内存根本没有发布

时间:2014-08-08 09:42:52

标签: python memory-leaks sqlalchemy pyramid

如何解决此内存泄漏?

我应采取哪些措施来清理旧的会话对象? 不是session.close()就足够了吗?

它与金字塔有关吗?

Sqlalchmey setup:
----------------------------------------------------------------------------------
def get_db(request):
    maker = request.registry.dbmaker
    session = maker()

    @profile
    def cleanup(request):
        _session = request.db
        if request.exception is not None:
            _session.rollback()
        else:
            _session.commit()
        _session.close()
        # del _session     # No memory released

    request.add_finished_callback(cleanup)
    return session

def main(global_config, **settings):
    :
    :
    config.registry.dbmaker = sessionmaker(bind=engine)
    config.add_request_method(get_db, name='db', reify=True)
    :
    :

金字塔应用请求处理程序就像

@view_config(route_name='list_employees', renderer='json')
def employees(request):
   session = request.db
   office = session.query(Office).get(1)
   employees = [x.name for x in office.employees]
   return employees

现在的问题是,在list_employees的每个请求中,内存都在增长。 内存增加的大小几乎等于office.employees.

的大小

调试:

request 1 starts with memory utilization = 10MB
request 1 ends with memory utilization = 18MB

request 2 starts with memory utilization = 18MB
request 2 ends with memory utilization = 26MB        

request 3 starts with memory utilization = 26MB
request 3 ends with memory utilization = 34MB        
                 :
                 :
           Grows eventually

employees = [x.name for x in office.employees]
This is the line where about 8-10MB memory utilized

要进行调试,我在Employ和Office模型中添加了 __ del __ 方法,看起来正在删除。

还尝试了session.expunge(office)del officegc.collect()

我使用https://pypi.python.org/pypi/memory_profiler调试内存消耗 另外我使用https://pypi.python.org/pypi/transaction是其他请求。

不使用调试金字塔工具栏。

编辑:发现此行的内存增加(employees = [x.name for x in office.employees])在6-7个请求后显示为零。但是查询返回了相同的行数。

编辑:添加了独立应用https://github.com/Narengowda/pyramid_sqlalchemy_app

编辑:它与SQLALCHEMY无关(我的不好)。 写了一个简单的视图函数,它没有任何sqlalchmey查询。

class Test(object):

    def __init__(self):
        self.x = 'sdfklhasdjkfhasklsdkjflksdfksd' *1000
        self.y = 'sdfklhasdjkfhasklsdkjflksdfksd' *1000
        self.z = 'sdfklhasdjkfhasklsdkjflksdfksd' *1000
        self.i = 'sdfklhasdjkfhasklsdkjflksdfksd' *1000
        self.v = 'sdfklhasdjkfhasklsdkjflksdfksd' *1000
        self.o = 'sdfklhasdjkfhasklsdkjflksdfksd' *1000


@view_config(route_name='home', renderer='json')
def my_view(request):
    return test(request)

@profile
def test(request):
    count = request.GET.get('count')
    l = [Test() for i in range(int(count))]
    print l[0]
    return {}

我能够看到这一点,下面是请求的日志

请求:1

行#内存使用增量行内容


23     37.3 MiB      0.0 MiB   @profile
24                             def test(request):
25     37.3 MiB      0.0 MiB       count = request.GET.get('count')
26    112.4 MiB     75.1 MiB       l = [Test() for i in range(int(count))]
27    112.4 MiB      0.0 MiB       print l[0]
28    112.4 MiB      0.0 MiB       return {}

请求:2

行#内存使用增量行内容


23    111.7 MiB      0.0 MiB   @profile
24                             def test(request):
25    111.7 MiB      0.0 MiB       count = request.GET.get('count')
26    187.3 MiB     75.6 MiB       l = [Test() for i in range(int(count))]
27    187.3 MiB      0.0 MiB       print l[0]
28    187.3 MiB      0.0 MiB       return {}

请求:3

行#内存使用增量行内容


23    184.3 MiB      0.0 MiB   @profile
24                             def test(request):
25    184.3 MiB      0.0 MiB       count = request.GET.get('count')
26    259.7 MiB     75.4 MiB       l = [Test() for i in range(int(count))]
27    259.7 MiB      0.0 MiB       print l[0]
28    259.7 MiB      0.0 MiB       return {}

请求:4

行#内存使用增量行内容


23    255.1 MiB      0.0 MiB   @profile
24                             def test(request):
25    255.1 MiB      0.0 MiB       count = request.GET.get('count')
26    330.4 MiB     75.3 MiB       l = [Test() for i in range(int(count))]
27    330.4 MiB      0.0 MiB       print l[0]
28    330.4 MiB      0.0 MiB       return {}

请求:5

行#内存使用增量行内容


23    328.2 MiB      0.0 MiB   @profile
24                             def test(request):
25    328.2 MiB      0.0 MiB       count = request.GET.get('count')
26    330.5 MiB      2.3 MiB       l = [Test() for i in range(int(count))]
27    330.5 MiB      0.0 MiB       print l[0]
28    330.5 MiB      0.0 MiB       return {}

请求:6

行#内存使用增量行内容


23    330.5 MiB      0.0 MiB   @profile
24                             def test(request):
25    330.5 MiB      0.0 MiB       count = request.GET.get('count')
26    330.5 MiB      0.0 MiB       l = [Test() for i in range(int(count))]
27    330.5 MiB      0.0 MiB       print l[0]
28    330.5 MiB      0.0 MiB       return {}

我已尝试多次使用不同的计数查询参数,看到内存利用率增加后,正好有5个请求(魔术)停止。

此外,我尝试打印所有对象并比较地址 我观察到的是看看请求4和5的日志。 看起来GC发生了,因此内存从330.4 Mi减少到328.2 MiB 但你不会看到75.3 MiB内存利用率来创建新对象(第26行),但你可以看到仅增加2.3 MiB。 后来我验证了最后两个请求中创建的对象的地址,发现最后两个请求中80%的对象地址是相同的

请求:4个对象地址

<pyramid_sqa.views.Test object at 0x3a042d0>
<pyramid_sqa.views.Test object at 0x3a04310>
<pyramid_sqa.views.Test object at 0x3a04350>
<pyramid_sqa.views.Test object at 0x3a04390>
<pyramid_sqa.views.Test object at 0x3a043d0>
<pyramid_sqa.views.Test object at 0x3a04410>
<pyramid_sqa.views.Test object at 0x3a04450>
<pyramid_sqa.views.Test object at 0x3a04490>
<pyramid_sqa.views.Test object at 0x3a044d0>
<pyramid_sqa.views.Test object at 0x3a04510>

请求:5个对象地址

<pyramid_sqa.views.Test object at 0x3a04390>
<pyramid_sqa.views.Test object at 0x3a043d0>
<pyramid_sqa.views.Test object at 0x3a04410>
<pyramid_sqa.views.Test object at 0x3a04450>
<pyramid_sqa.views.Test object at 0x3a04490>
<pyramid_sqa.views.Test object at 0x3a044d0>
<pyramid_sqa.views.Test object at 0x3a04290>
<pyramid_sqa.views.Test object at 0x3a04550>
<pyramid_sqa.views.Test object at 0x3a04590>
<pyramid_sqa.views.Test object at 0x3a045d0>

因此创建了新对象并且python正在重用内存(重用对象!!?)

如果我的服务器内存会像这样拍摄内存吗?

2 个答案:

答案 0 :(得分:1)

Python为Python对象做了自己的内存管理,即使CPython GC释放了Python对象,它仍然不会将内存释放到操作系统(如malloc()/ free()可能会这样做)。当GC释放Python对象时,内存可以用于新的Python对象。这是当请求编号6的内存消耗没有增加时看到的效果。在编号5之后,GC释放已删除的对象,请求编号6中的新对象可以使用释放的内存。

因此,您没有内存泄漏,您刚刚发现了CPython内存管理的工作原理。内存消耗不会无限增长。

答案 1 :(得分:0)

这就是python如何工作,以及python如何进行内存管理,所以从技术上讲,这不是问题。

现在,我可以给你写一个无聊的段落,详细说明为什么会这样,但这个题目太大了,你肯定不想搞乱内存管理。但是,我找到了一个网站,以一种易于理解的格式很好地总结了所有内容:here it is

说到这一点,你可以尝试的唯一可行但可能不起作用的是使用python Garbage Collector Interface强制来释放未使用的东西记忆,像这样:

gc.collect()

然而,不是强调'强制',因为你通常不应该使用Python GC,因为它知道如何完成它的工作,并且在大多数情况下,它做得很好,但是如果它是你唯一的选择,它可能值得一试。