"""Test for memory leak in CPython 3.6b1 Popping an attribute off of `self.__dict__` causes unbounded memory growth. It only takes about a dozen iterations to use up several GB of memory. """ from contextlib import contextmanager import time class Test(object): def __init__(self): # Critical part 1: # calling this in `__init__` causes later calls to result in unbounded memory growth # even across instances. # If not called in `__init__`, counter resets for each instance, # but bug remains. self.__dict__ = {} self.trigger_bug() def trigger_bug(self): """Set an attribute and then remove it from self.__dict__""" # Critical part 2: # This dict.pop is more expensive each time it is called. # The cost does not reset per Test instance, it grows continuously. print(len(self.__dict__)) self.attr = 1 self.__dict__.pop('attr', None) @contextmanager def timer(): tic = time.monotonic() try: yield finally: dt = time.monotonic() - tic print('%g' % dt) print("Reusing Test instance") obj = Test() for i in range(100): print('\n%i' % i, end=' ') with timer(): obj.trigger_bug() # the problem accrues even across instances print("New test instance each time") for i in range(200): print('\n%i' % i, end=' ') with timer(): obj = Test() obj.trigger_bug()