How to solve this memory leak?
What measures should be taken to clean up old session objects? Is session.close() enough?
or
Is this something related to the pyramid?
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()
Pyramid's application request handler is similar to
@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
Now the problem is that in every list_employees request, memory is growing. the size of the increase in memory is almost equal to the size of office.employees.
Debug:
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
To debug, I added the __ del __ method in the Employ and Office models, it looks like they are being deleted.
Also tried session.expunge(office) , del office and gc.collect()
I am debugging memory consumption using https://pypi.python.org/pypi/memory_profiler Also I use https://pypi.python.org/pypi/transaction other requests.
Do not use the toolbar to debug the pyramid.
EDIT : An increase in memory was detected on this line (employees = [x.name for x in office.employees]) shows zero after 6-7 queries. But the query returned the same number of rows.
EDIT : Added standalone app https://github.com/Narengowda/pyramid_sqlalchemy_app
EDIT : ITS NOT RELATED TO SQLALCHEMY FOR EVERYONE (my bad). Wrote a simple view function that has no sqlalchmey queries.
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 {}
I can see it below, magazines magazines
REQUEST: 1
Line # Memory Usage Increase Line Contents
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 {}
REQUEST: 2
Line # Memory Usage Increase Line Contents
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 {}
REQUEST: 3
Line # Memory Usage Increase Line Contents
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 {}
REQUEST: 4
Line # Memory Usage Increase Line Contents
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 {}
REQUEST: 5
Line # Memory Usage Increase Line Contents
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 {}
REQUEST: 6
Line # Memory Usage Increase Line Contents
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 {}
I tried many times with a different count request parameter, the increase in memory load stops after exactly 5 requests (magic).
I also tried to print all the objects and compare their addresses, what I observed was to look at the request logs 4 and 5. It seems that GC happened, so the memory decreased from 330.4 Mi to 328.2 MiB But you can’t use 75.3 Usage MiB memory to create new objects (line 26), but you can only see 2.3 MiB increases. Later I checked the address of the objects created in the last two queries, found 80% of the objects from the last two queries are the same
REQUEST: 4 addresses of objects
<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>
REQUEST: 5 addresses of objects
<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>
so new objects are created and python reuses memory (reusing an object !!?)
Is everything all right if my server memory blows through such memory?