Python-pyramid application memory not released at all

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() # 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) : : 

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?

+7
python pyramid memory-leaks sqlalchemy
source share
2 answers

Python does its own memory management for Python objects, and even when CPython GC frees a Python object, it still won't free up operating system memory (as malloc () / free () can). When the GC frees a Python object, memory can then be used for new Python objects. This is the effect you see when memory consumption does not increase for query number 6. After number 5, the GC freed the deleted objects, and the new object in query number 6 could use the freed memory.

This way you have no memory leak, you just learned how CPython memory management works. Memory consumption is not growing without restrictions.

+1
source share

This is how python works, and how python does its memory management, so technically speaking, this is not a problem.

Now I can write you a boring paragraph, I will explain in more detail why this is so, but this is too big a topic, and you definitely do not want to interfere with memory management. However, I found a website that summarizes it all in an easy-to-understand format: here it is

With that said, the only thing you can try, which may work, but perhaps also may not work, is to use the python Garbage Collector Interface and force free unused memory, for example:

 gc.collect() 

However, there is no emphasis on “strength,” since you generally don’t interfere with the Python GC because it knows how to do its job, and in most cases it’s good, but if this is your only option, it may be worth it.

0
source share

All Articles