forked from tornadoweb/tornado
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcirclerefs.py
109 lines (86 loc) · 3.02 KB
/
circlerefs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#!/usr/bin/env python
"""Test script to find circular references.
Circular references are not leaks per se, because they will eventually
be GC'd. However, on CPython, they prevent the reference-counting fast
path from being used and instead rely on the slower full GC. This
increases memory footprint and CPU overhead, so we try to eliminate
circular references created by normal operation.
"""
from __future__ import print_function
import gc
import traceback
import types
from tornado import web, ioloop, gen, httpclient
def find_circular_references(garbage=None):
def inner(level):
for item in level:
item_id = id(item)
if item_id not in garbage_ids:
continue
if item_id in visited_ids:
continue
if item_id in stack_ids:
candidate = stack[stack.index(item):]
candidate.append(item)
found.append(candidate)
continue
stack.append(item)
stack_ids.add(item_id)
inner(gc.get_referents(item))
stack.pop()
stack_ids.remove(item_id)
visited_ids.add(item_id)
garbage = garbage or gc.garbage
found = []
stack = []
stack_ids = set()
garbage_ids = set(map(id, garbage))
visited_ids = set()
inner(garbage)
inner = None
return found
class CollectHandler(web.RequestHandler):
@gen.coroutine
def get(self):
self.write("Collected: {}\n".format(gc.collect()))
self.write("Garbage: {}\n".format(len(gc.garbage)))
for circular in find_circular_references():
print('\n==========\n Circular \n==========')
for item in circular:
print(' ', repr(item))
for item in circular:
if isinstance(item, types.FrameType):
print('\nLocals:', item.f_locals)
print('\nTraceback:', repr(item))
traceback.print_stack(item)
class DummyHandler(web.RequestHandler):
@gen.coroutine
def get(self):
self.write('ok\n')
class DummyAsyncHandler(web.RequestHandler):
@gen.coroutine
def get(self):
raise web.Finish('ok\n')
application = web.Application([
(r'/dummy/', DummyHandler),
(r'/dummyasync/', DummyAsyncHandler),
(r'/collect/', CollectHandler),
], debug=True)
@gen.coroutine
def main():
gc.disable()
gc.collect()
gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_SAVEALL)
print('GC disabled')
print("Start on 8888")
application.listen(8888, '127.0.0.1')
# Do a little work. Alternately, could leave this script running and
# poke at it with a browser.
client = httpclient.AsyncHTTPClient()
yield client.fetch('http://127.0.0.1:8888/dummy/')
yield client.fetch('http://127.0.0.1:8888/dummyasync/', raise_error=False)
# Now report on the results.
resp = yield client.fetch('http://127.0.0.1:8888/collect/')
print(resp.body)
if __name__ == "__main__":
ioloop.IOLoop.current().run_sync(main)