Не думаю, что проблема связана с exec
- сборщик мусора просто не активируется.Если вы извлечете код exec
'в основное приложение, оба способа будут иметь то же поведение, что и exec
:
class test:
def __init__(self):
self.buf = '1'x
1024*200
x = test()
# Consumes 200MB
class test:
def __init__(self):
self.buf = '1'*1024*1024*200
def f1(): x = test()
f1()
# Memory get collected correctly
Разница между этими двумя методами заключается в том, что во втором методелокальная область видимости изменяется при вызове f1()
, и я думаю, что сборщик мусора запускается, когда x
выходит из области видимости, поскольку функция возвращает управление в основной сценарий.Если область действия не изменяется, то сборщик мусора ждет , пока разница между числом выделений и числом освобождений не превысит его пороговое значение (на моем компьютере пороговое значение равно 700, по умолчанию работает Python 2.7).
Мы можем немного понять, что происходит:
import sys
import gc
class test:
def __init__(self):
self.buf = '1'*1024*1024*200
x = test()
print gc.get_count()
# Prints (168, 8, 0)
Итак, мы видим, что сборщик мусора запускается много раз, но по какой-то причине не собирает x
.Если вы тестируете с другой версией:
import sys
import gc
class test:
def __init__(self):
self.buf = '1'*1024*1024*200
def f1(): x = test()
f1()
print gc.get_count()
# Prints (172, 8, 0)
В этом случае мы знаем, что ему действительно удалось собрать x
.Таким образом, кажется, что когда x
объявлен в глобальной области видимости, он сохраняет некоторую циклическую ссылку на себя, которая предотвращает его сбор.Мы всегда можем использовать del x
для принудительного принудительного сбора, но, конечно, это не идеально.Если вы используете gc.get_referrers(x)
, мы сможем увидеть, какие объекты все еще ссылаются на *1024*, и, возможно, это даст ключ к пониманию того, как этого избежать.
Я знаю, что не зналдействительно решить проблему, но, надеюсь, это помогло вам в правильном направлении.Я запомню этот вопрос на случай, если найду что-нибудь позже.