django.dispatch.saferef.BoundMethodWeakRef object leak for each request resulting in memory leak

StackOverflow https://stackoverflow.com/questions/23448404

  •  14-07-2023
  •  | 
  •  

Question

A django webapp is leaking memory. I have tried suggestions here: https://docs.djangoproject.com/en/dev/faq/models/#why-is-django-leaking-memory even though the app is not standalone.

I used guppy (by following this nice page: http://www.smira.ru/wp-content/uploads/2011/08/heapy.html) to analyze the heap and I see a correlation between requests and a leak of django.dispatch.saferef.BoundMethodWeakRef objects:

Partition of a set of 146642 objects. Total size = 10765760 bytes.
 Index  Count   %     Size   % Cumulative  % Referrers by Kind (class / dict of class)
     0  49064  33  2914412  27   2914412  27 types.CodeType
     1  21631  15   737184   7   3651596  34 tuple
     2  12883   9   727296   7   4378892  41 function
     3   5505   4   583728   5   4962620  46 dict of module
     4   3163   2   497232   5   5459852  51 type
     5   8192   6   481600   4   5941452  55 dict of type
     6    328   0   424000   4   6365452  59 function, module
     7    775   1   403000   4   6768452  63 django.dispatch.saferef.BoundMethodWeakref
     8   1597   1   351728   3   7120180  66 function, tuple
     9   6015   4   270704   3   7390884  69 dict (no owner)
<1864 more rows. Type e.g. '_.more' to view.>
Reference Pattern by <[dict of] class>.
 0: _ --- [-] 775 (<referred by: <Nothing>> | <referred by: django.dispatch.s...
 1: a      [-] 774 django.dispatch.saferef.BoundMethodWeakref: 0x84696cc...
 2: aa ---- [-] 1549 tuple: 0x846908c*1, 0x846910c*2, 0x846920c*1...
 3: a3       [-] 774 function: django.dispatch.saferef.remove...
 4: a4 ------ [-] 1548 __builtin__.weakref: 0x847539c, 0x8475a04, 0x8475b44...
 5: a5         [-] 774 dict of django.dispatch.saferef.BoundMethodWeakref: 0x...
 6: a6 -------- [^ 1] 774 django.dispatch.saferef.BoundMethodWeakref: 0x84696...
 7: aab      [-] 3 list: 0xb6e7156cL*1, 0xb6e7168cL*773, 0xb701684cL*2
 8: aaba ---- [-] 3 dict of django.dispatch.dispatcher.Signal: 0xb6ee1c6cL...
 9: aabaa      [-] 3 django.dispatch.dispatcher.Signal: 0xb6ee1c6cL...
<Type e.g. '_.more' for more.>
10: aaba3 ----- [S] 3 dict of module: ..models, ..signals, ..signals
11: aabaab      [-] 3 types.MethodType: <django.dispatch.dispatcher.Signal at...
12: aabaaba ---- [-] 2 list: 0xa12888c*1, 0xa13896c*1
13: aabaabaa      [-] 2 dict of django.dispatch.saferef.BoundMethodWeakref: 0...
14: aabaaba3 ----- [-] 2 django.dispatch.saferef.BoundMethodWeakref: 0x9e9f46...
15: aabaaba4        [-] 5 tuple: 0xa15154c*1, 0xa15186c*2, 0xa1531ac*2...
16: aabaaba5 ------- [^ 7] 3 list: 0xb6e7156cL*1, 0xb6e7168cL*773...
17: aabaaba4b        [-] 2 function: django.dispatch.saferef.remove...
18: aabaaba4ba ------ [-] 4 __builtin__.weakref: 0x98e90cc, 0x98e9734...
19: aabaaba4baa        [^ 13] 2 dict of django.dispatch.saferef.BoundMethodWe...

Note that django.dispatch.saferef.BoundMethodWeakref goes up in count(775 here) with each request.

I am kind of new to django, but something that I think is done differently in this app is that a reference to an object and a flag are added to each request object in process_request using a custom middleware class. I am not sure if the reference is causing django to not free up the request's resources somehow.

Here is the code tagging on the reference and the flag:

class ApplianceMiddleware(object):

    def process_request(self, request):
    request.__class__.appliance = get_appliance()
    # do heap analysis across requests to check for memory leaks - heap
    # size across requests should be constant
    heap_analysis.analyse()

    request.__class__.from_int_if = False

    # Continue processing
    return None

Does anyone know what could be causing this? If it is the reference being added to the request that is causing this, where can I clean up? Maybe by keeping a reference to the request object and deleting the reference to the appliance object when processing the next request?

Was it helpful?

Solution

It turned out that there were signal handlers being registered for request_finish, post_save, and post_modify. They were getting 'connected' for each request and they were sticking around after. A disconnect when handling the signal solved the memory leak.

The clue for it being related to signals was in the heapy heap info. e.g.:

aabaa      [-] 3 django.dispatch.dispatcher.Signal: 0xb6ee1c6cL...
10: aaba3 ----- [S] 3 dict of module: ..models, ..signals, ..signals
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top