Hunting a memory leak
Francesc Alted
falted at openlc.org
Fri Aug 29 14:33:45 EDT 2003
[Ooops. Something went wrong with my newsreader config ;-)]
Thanks for the responses!. I started by fetching the TrackRefs() class
from http://cvs.zope.org/Zope3/test.py and pasted it in my local copy
of unittest.py. Then, I've modified the TestCase.__call__ try: block
from the original:
try:
testMethod()
ok = 1
to read:
try:
rc1 = rc2 = None
#Pre-heating
for i in xrange(10):
testMethod()
gc.collect()
rc1 = sys.gettotalrefcount()
track = TrackRefs()
# Second (first "valid") loop
for i in xrange(10):
testMethod()
gc.collect()
rc2 = sys.gettotalrefcount()
print "First output of TrackRefs:"
track.update()
print >>sys.stderr, "%5d %s.%s.%s()" % (rc2-rc1,
testMethod.__module__, testMethod.im_class.__name__,
testMethod.im_func.__name__)
# Third loop
for i in xrange(10):
testMethod()
gc.collect()
rc3 = sys.gettotalrefcount()
print "Second output of TrackRefs:"
track.update()
print >>sys.stderr, "%5d %s.%s.%s()" % (rc3-rc2,
testMethod.__module__, testMethod.im_class.__name__,
testMethod.im_func.__name__)
ok = 1
However, I'm not sure if I have made a good implementation. My
understanding is that the first loop is for pre-heating (to avoid
false count-refs due to cache issues and so). The second loop should
already give good count references and, thereby, I've made a call to
track.update(). Finally, I wanted to re-check the results of the
second loop with a third one. Therefore, I expected more or less the
same results in second and third loops.
But... the results are different!. Following are the results of this run:
$ python2.3 widetree3.py
First output of TrackRefs:
<type 'str'> 13032 85335
<type 'tuple'> 8969 38402
<type 'Cfunc'> 1761 11931
<type 'code'> 1215 4871
<type 'function'> 1180 5189
<type 'dict'> 841 4897
<type 'builtin_function_or_method'> 516 2781
<type 'int'> 331 3597
<type 'wrapper_descriptor'> 295 1180
<type 'method_descriptor'> 236 944
<type 'classobj'> 145 1092
<type 'module'> 107 734
<type 'list'> 94 440
<type 'type'> 86 1967
<type 'getset_descriptor'> 84 336
<type 'weakref'> 75 306
<type 'float'> 73 312
<type 'member_descriptor'> 70 280
<type 'ufunc'> 52 364
<type 'instance'> 42 435
<type 'instancemethod'> 41 164
<class 'numarray.ufunc._BinaryUFunc'> 25 187
<class 'numarray.ufunc._UnaryUFunc'> 24 173
<type 'frame'> 9 44
<type 'long'> 7 28
<type 'property'> 6 25
<type 'PyCObject'> 4 20
<class 'unittest.TestSuite'> 3 31
<type 'file'> 3 23
<type 'listiterator'> 3 12
<type 'bool'> 2 41
<class 'random.Random'> 2 30
<type '_sre.SRE_Pattern'> 2 9
<type 'complex'> 2 8
<type 'thread.lock'> 2 8
<type 'NoneType'> 1 2371
<class 'unittest._TextTestResult'> 1 16
<type 'ellipsis'> 1 12
<class '__main__.WideTreeTestCase'> 1 11
<class 'tables.IsDescription.metaIsDescription'> 1 10
<class 'unittest.TestProgram'> 1 9
<class 'numarray.ufunc._ChooseUFunc'> 1 8
<class 'unittest.TestLoader'> 1 7
<class 'unittest.TrackRefs'> 1 6
<class 'unittest.TextTestRunner'> 1 6
<type 'NotImplementedType'> 1 6
<class 'numarray.ufunc._PutUFunc'> 1 5
<class 'numarray.ufunc._TakeUFunc'> 1 5
<class 'unittest._WritelnDecorator'> 1 5
<type 'staticmethod'> 1 4
<type 'classmethod'> 1 4
<type 'classmethod_descriptor'> 1 4
<type 'unicode'> 1 4
7 __main__.WideTreeTestCase.test00_Leafs()
Second output of TrackRefs:
<type 'int'> 37 218
<type 'type'> 0 74
212 __main__.WideTreeTestCase.test00_Leafs()
.
----------------------------------------------------------------------
Ran 1 test in 0.689s
OK
[21397 refs]
$
As you can see, for the second loop (first output of TrackRefs), a lot
of objects appear, but after the third loop (second output of
TrackRefs), much less appear (only objects of type "int" and
"type"). Besides, the increment of the total references for the second
loop is only 7 while for the third loop is 212. Finally, to add even
more confusion, these numbers are *totally* independent of the number
of iterations I put in the loops. You see 10 in the code, but you can
try with 100 (in one or all the loops) and you get exactly the same
figures.
I definitely think that I have made a bad implementation of the try:
code block, but I can't figure out what's going wrong.
I would appreciate some ideas.
Francesc Alted
More information about the Python-list
mailing list