Разгрузите модуль в Python

TL/DR:

import gc, sys

print len(gc.get_objects()) # 4073 objects in memory

# Attempt to unload the module

import httplib
del sys.modules["httplib"]
httplib = None

gc.collect()
print len(gc.get_objects()) # 6745 objects in memory

ОБНОВЛЕНИЕ я связался с разработчиками Python об этой проблеме и действительно не будет возможным разгрузить модуль полностью "за следующие пять лет". (см. ссылку),

Признайте, что Python действительно не поддерживает разгрузку модулей для серьезных, фундаментальных, непреодолимых, технических проблем, в 2.x.


Во время моего недавнего поиска на memleak в моем приложении я сузил его к модулям, а именно, моя неспособность собрать "мусор" разгруженный модуль. Используя любой метод, упомянутый ниже для разгрузки модуля, оставляет тысячи объектов в памяти. Другими словами - я не могу разгрузить модуль в Python...

Остальная часть вопроса является попыткой собрать "мусор" модуль так или иначе.

Давайте попробуем:

import gc
import sys

sm = sys.modules.copy()  # httplib, which we'll try to unload isn't yet 
                         # in sys.modules, so, this isn't the source of problem

print len(gc.get_objects()) # 4074 objects in memory

Давайте сохраним копию sys.modules попытаться восстановить его позже. Так, это - базовая линия 4 074 объекта. Мы должны идеально возвратиться к этому так или иначе.

Давайте импортируем модуль:

import httplib
print len(gc.get_objects()) # 7063 objects in memory

Мы до 7K объектов немусора. Давайте попытаемся удалить httplib от sys.modules.

sys.modules.pop('httplib')
gc.collect()
print len(gc.get_objects()) # 7063 objects in memory

Ну, это не работало. Хм, но нет ли ссылка в __main__? О, да:

del httplib
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory

Ура, вниз 300 объектов. Однако, никакая сигара, это - путь больше чем 4 000 исходных объектов. Давайте попытаемся восстановить sys.modules из копии.

sys.modules = sm
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory

Хм, хорошо это было бессмысленно, никакое изменение.. Возможно, если мы вытираем globals...

globals().clear()
import gc # we need this since gc was in globals() too
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory

местные жители?

locals().clear()
import gc # we need this since gc was in globals() too
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory

Что.. что, если мы imported модуль в exec?

local_dict = {}
exec 'import httplib' in local_dict
del local_dict
gc.collect()
print len(gc.get_objects())  # back to 7063 objects in memory

Теперь, это несправедливо, это импортировало его в __main__, почему? Это никогда не должно было уезжать local_dict... Argh! Мы отступаем к полностью импортированному httplib. Возможно, если мы заменили его фиктивным объектом?

from types import ModuleType
import sys
print len(gc.get_objects())  # 7064 objects in memory

Чертовски.....!!

sys.modules['httplib'] = ModuleType('httplib')
print len(gc.get_objects())  # 7066 objects in memory

Умрите модули, умрите!!

import httplib
for attr in dir(httplib):
    setattr(httplib, attr, None)
gc.collect()
print len(gc.get_objects())  # 6749 objects in memory

Хорошо, после всех попыток, лучшее +2675 (почти +50%) от начальной точки... Это только от одного модуля... Это ничего даже не имеет большим внутри...

Хорошо, теперь серьезно, где моя ошибка? Как я разгружаю модуль и вытираю весь, его - содержание? Или являются модули Python одной гигантской утечкой памяти?

Полный источник в более простом для копирования формы: http://gist.github.com/450606

53
задан unutbu 8 June 2016 в 02:08
поделиться

3 ответа

Python не поддерживает выгрузку модулей.

Однако, если ваша программа не загружает неограниченное количество модулей с течением времени, это не является источником утечки памяти. Модули обычно загружаются один раз при запуске и все. Скорее всего, утечка памяти происходит в другом месте.

В маловероятном случае, если ваша программа действительно загружает неограниченное количество модулей с течением времени, вам, вероятно, следует перепроектировать свою программу. ;-)

.
16
ответ дан 7 November 2019 в 08:54
поделиться

Я не уверен насчет Python, но на других языках вызов эквивалента gc.collect () не не освобождает неиспользуемую память - он освобождает эту память, только если / когда память действительно нужна .

В противном случае для Python имеет смысл временно оставить модули в памяти на случай, если их потребуется снова загрузить.

3
ответ дан 7 November 2019 в 08:54
поделиться

(Вам следует попытаться написать более сжатые вопросы; я прочитал только начало и просмотрел все остальное.) Вначале я вижу простую проблему:

sm = sys.modules.copy()

Вы сделали копию sys.modules, так что теперь ваш copy имеет ссылку на модуль - поэтому, конечно, он не будет собран. Вы можете увидеть, что к нему относится, с помощью gc.get_referrers.

Это отлично работает:

# module1.py
class test(object):
    def __del__(self):
        print "unloaded module1"
a = test()

print "loaded module1"

.

# testing.py
def run():
    print "importing module1"
    import module1
    print "finished importing module1"

def main():
    run()
    import sys
    del sys.modules["module1"]
    print "finished"

if __name__ == '__main__':
    main()

module1 выгружается, как только мы удаляем его из sys.modules, потому что не осталось ссылок на модуль. (Выполнение module1 = None после импорта тоже будет работать - я просто поместил импорт в другую функцию для ясности. Все, что вам нужно сделать, это отбросить ссылки на него.)

Теперь это На практике это немного сложно сделать из-за двух проблем:

  • Чтобы собрать модуль, все ссылки на модуль должны быть недоступны (как при сборе любого объекта). Это означает, что любые другие модули, которые его импортировали, также необходимо разыменовать и перезагрузить.
  • Если вы удалите модуль из sys.modules, когда на него все еще есть ссылка в другом месте, вы создадите необычную ситуацию: модуль все еще загружается и используется кодом, но загрузчик модуля больше не знает об этом. В следующий раз, когда вы импортируете модуль, вы не получите ссылку на существующий (поскольку вы удалили его запись), поэтому он загрузит вторую сосуществующую копию модуля. Это может вызвать серьезные проблемы с согласованностью. Итак, убедитесь, что на модуль не осталось ссылок, прежде чем окончательно удалить его из sys.modules.

В общем случае есть несколько сложных проблем: определение того, какие модули зависят от модуля, который вы выгружаете; знание того, можно ли их выгружать (сильно зависит от вашего варианта использования); обработка потоков при изучении всего этого (взгляните на imp.acquire_lock) и так далее.

Я мог бы придумать случай, когда это могло бы быть полезно, но в большинстве случаев я бы рекомендовал просто перезапустить приложение, если его код изменится. Скорее всего, у вас просто начнется головная боль.

0
ответ дан 7 November 2019 в 08:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: