Подтвердить что ты не робот

Выгрузить модуль в 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, чтобы попытаться восстановить ее позже. Итак, это базовые 4074 объектов. Мы должны в идеале вернуться к этому как-то.

Можно импортировать модуль:

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 объектов. Тем не менее, нет сигары, таким образом, более 4000 оригинальных предметов. Попробуйте восстановить sys.modules из копии.

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

Хммм, ну это было бессмысленно, без изменений.. Может быть, если мы уничтожим глобалы...

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

sm = sys.modules.copy()

Вы сделали копию sys.modules, так что теперь ваша копия имеет ссылку на модуль - так что, конечно, она не будет собрана. Вы можете видеть, что относится к нему с помощью 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) и т.д.

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