I'm still gathering data, but it appears as though recent flask-debugtoolbar versions cause a memory leak. After I upgraded to Flask and Werkzeug 0.8 I also had to update flask-debugtoolbar from baecf852dda0f5b6c4700fb6deaeda4068b3a19c to current master (45ce65c058a580d80e2ac207689601edef5db24b), I've been noticing memory usage growing on every request made against the server.
I've disabled my custom panels and the ones from MongoEngine but left TimerDebugPanel, HeaderDebugPanel, RequestVarsDebugPanel, TemplateDebugPanel, LoggingPanel and ProfilerDebugPanel enabled.
Here is the output from heapy for a run with the debug panel disabled with DEBUG_TB_ENABLED=False
. The first set is the total number of objects by type. The second set is the types of the objects that refer to the ones in the front. If you are familiar with heapy, the former is heap()
and the latter is heap().byrcs
L
Partition of a set of 202 objects. Total size = 40656 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 11 5 9224 23 9224 23 dict (no owner)
1 16 8 8456 21 17680 43 types.FrameType
2 59 29 5128 13 22808 56 str
3 30 15 3728 9 26536 65 unicode
4 16 8 1600 4 28136 69 list
5 1 0 1048 3 29184 72 dict of flask.ctx.RequestContext
6 1 0 1048 3 30232 74 dict of flask.wrappers.Request
7 1 0 1048 3 31280 77 dict of flask.wrappers.Response
8 1 0 1048 3 32328 80 dict of mimetools.Message
9 1 0 1048 3 33376 82 dict of werkzeug.routing.MapAdapter
<35 more rows. Type e.g. '_.more' to view.>
Partition of a set of 202 objects. Total size = 40656 bytes.
Index Count % Size % Cumulative % Referrers by Kind (class / dict of class)
0 22 11 7912 19 7912 19 types.FrameType
1 67 33 7736 19 15648 38 dict (no owner)
2 1 0 3352 8 19000 47 __builtin__.cell, dict of flask.wrappers.Request, dict
of werkzeug.datastructures.EnvironHeaders,
types.FrameType
3 17 8 3136 8 22136 54 list
4 6 3 1472 4 23608 58 dict of mimetools.Message
5 2 1 1120 3 24728 61 dict of 0x7fca19073b90
6 1 0 1048 3 25776 63 flask.ctx.RequestContext
7 1 0 1048 3 26824 66 flask.wrappers.Request
8 1 0 1048 3 27872 69 flask.wrappers.Response
9 1 0 1048 3 28920 71 mimetools.Message
<41 more rows. Type e.g. '_.more' to view.>
A run with the debug toolbar enabled:
Partition of a set of 538 objects. Total size = 81920 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 35 7 20552 25 20552 25 dict (no owner)
1 135 25 11128 14 31680 39 str
2 122 23 10472 13 42152 51 tuple
3 16 3 8208 10 50360 61 types.FrameType
4 34 6 4064 5 54424 66 unicode
5 31 6 4024 5 58448 71 list
6 46 9 1104 1 59552 73 int
7 1 0 1048 1 60600 74 dict of blinker._saferef.BoundMethodWeakref
8 1 0 1048 1 61648 75 dict of flask.ctx.RequestContext
9 1 0 1048 1 62696 77 dict of flask.wrappers.Request
<79 more rows. Type e.g. '_.more' to view.>
Partition of a set of 538 objects. Total size = 81920 bytes.
Index Count % Size % Cumulative % Referrers by Kind (class / dict of class)
0 143 27 16712 20 16712 20 list
1 30 6 12544 15 29256 36 types.FrameType
2 83 15 10000 12 39256 48 dict (no owner)
3 116 22 6592 8 45848 56 tuple
4 1 0 3352 4 49200 60 __builtin__.cell, dict (no owner), dict of
flask.wrappers.Request, dict of
werkzeug.datastructures.EnvironHeaders, types.FrameType
5 6 1 1472 2 50672 62 dict of mimetools.Message
6 2 0 1120 1 51792 63 dict of 0x7f9d02073f00
7 1 0 1048 1 52840 65 blinker._saferef.BoundMethodWeakref
8 1 0 1048 1 53888 66 dict of
flask_debugtoolbar.panels.headers.HeaderDebugPanel
9 1 0 1048 1 54936 67 flask.ctx.RequestContext
<84 more rows. Type e.g. '_.more' to view.>
Hopefully you can see that the dict, str, and tuple usage is much larger with the debug toolbar, and that list objects seem to be holding a large number of references, with dict also in there. Is it possible that the toolbar is holding onto references that isn't being released?
I'm going to investigate a bit more and see if there is anything that stands out in the diffs between the two commits I referenced earlier, and to see if it seems to be an issue with a particular panel.