web-dev-qa-db-fra.com

Déchargez un module dans 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

METTRE À JOUR J'ai contacté Python développeurs à propos de ce problème et en effet c'est il ne sera pas possible de décharger un module complètement "dans les cinq prochaines années". (Voir le lien)

Veuillez accepter que Python ne prend en effet pas en charge le déchargement des modules pour les problèmes techniques graves, fondamentaux, insurmontables, dans 2.x.


Au cours de ma récente recherche d'un memleak dans mon application, je l'ai réduit aux modules, à savoir mon incapacité à garbage collection un module déchargé. L'utilisation de n'importe quelle méthode répertoriée ci-dessous pour décharger un module laisse des milliers d'objets en mémoire. En d'autres termes - je ne peux pas décharger un module en Python ...

Le reste de la question est de tenter de ramasser un module d'une manière ou d'une autre.

Essayons:

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

Sauvegardons une copie de sys.modules pour tenter de le restaurer ultérieurement. Donc, ceci est une base de 4074 objets. Nous devrions idéalement y revenir d'une manière ou d'une autre.

Importons un module:

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

Nous sommes jusqu'à 7K objets non-garbage. Essayons de supprimer httplib de sys.modules.

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

Eh bien, cela n'a pas fonctionné. Hmm, mais n'y a-t-il pas de référence dans __main__? Oh oui:

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

Hourra, en baisse de 300 objets. Pourtant, pas de cigare, c'est bien plus de 4000 objets originaux. Essayons de restaurer sys.modules à partir de la copie.

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

Hmmm, eh bien c'était inutile, pas de changement .. Peut-être que si nous éliminons les globaux ...

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

des locaux?

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

Qu'est-ce que .. et si nous imported un module à l'intérieur de 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

Maintenant, ce n'est pas juste, il l'a importé dans __main__, Pourquoi? Il n'aurait jamais dû quitter le local_dict... Argh! Nous revenons à httplib entièrement importés. Peut-être que si nous le remplaçions par un objet factice?

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

Sanglant.....!!

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

Meurs modules, meurs !!

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

D'accord, après toutes les tentatives, le meilleur est +2675 (presque + 50%) du point de départ ... C'est juste à partir d'un module ... Cela n'a même rien de gros à l'intérieur ...

Ok, maintenant sérieusement, où est mon erreur? Comment décharger un module et effacer tout son contenu? Ou les modules de Python sont-ils une fuite de mémoire géante?

Source complète sous une forme plus simple à copier: http://Gist.github.com/450606

54
Slava V

Python ne prend pas en charge le déchargement des modules.

Cependant, à moins que votre programme ne charge un nombre illimité de modules au fil du temps, ce n'est pas la source de votre fuite de mémoire. Les modules sont normalement chargés une fois au démarrage et c'est tout. Votre fuite de mémoire se situe probablement ailleurs.

Dans le cas peu probable où votre programme charge réellement un nombre illimité de modules au fil du temps, vous devriez probablement reconcevoir votre programme. ;-)

16
Daniel Stutzbach

Je ne suis pas sûr de Python, mais dans d'autres langages, appeler l'équivalent de gc.collect()pas libère la mémoire inutilisée - cela ne libérera cette mémoire que si/quand la mémoire est réellement nécessaire.

Sinon, il est logique que Python) garde les modules en mémoire pour le moment, au cas où ils auraient besoin d'être à nouveau chargés.

Python's small object manager rarely returns memory back to the Operating System. De ici et ici . Donc, à proprement parler, python a (par conception) une sorte de fuite de mémoire, même lorsque les objets sont "collectés par gc".

0
ilias iliadis

(Vous devriez essayer d'écrire des questions plus concises; je n'ai lu que le début et j'ai parcouru le reste.) Je vois un problème simple au début:

sm = sys.modules.copy()

Vous avez fait une copie de sys.modules, donc maintenant votre copie a une référence au module - donc bien sûr, elle ne sera pas collectée. Vous pouvez voir ce qui y fait référence avec gc.get_referrers.

Cela fonctionne bien:

# 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 __== '__main__':
    main()

module1 est déchargé dès que nous le supprimons de sys.modules, car il n'y a plus de références au module. (Faire module1 = None après l'importation fonctionnerait aussi - je viens de mettre l'importation dans une autre fonction pour plus de clarté. Tout ce que vous avez à faire est de laisser tomber vos références.)

Maintenant, c'est un peu difficile de faire cela en pratique, à cause de deux problèmes:

  • Afin de collecter un module, toutes les références au module doivent être inaccessibles (comme pour la collecte de tout objet). Cela signifie que tous les autres modules qui l'ont importé doivent également être déréférencés et rechargés.
  • Si vous supprimez un module de sys.modules alors qu'il est encore référencé ailleurs, vous avez créé une situation inhabituelle: le module est toujours chargé et utilisé par code, mais le chargeur de module ne le sait plus. La prochaine fois que vous importerez le module, vous n'obtiendrez pas de référence à l'existant (puisque vous en avez supprimé l'enregistrement), donc il chargera une deuxième copie coexistante du module. Cela peut entraîner de graves problèmes de cohérence. Assurez-vous donc qu'il ne reste aucune référence au module avant de le supprimer définitivement de sys.modules.

Il y a quelques problèmes délicats à utiliser en général: détecter quels modules dépendent du module que vous déchargez; savoir s'il est acceptable de les décharger aussi (dépend fortement de votre cas d'utilisation); gérer les threads tout en examinant tout cela (jetez un œil à imp.acquire_lock), et ainsi de suite.

Je pourrais trouver un cas où cela pourrait être utile, mais la plupart du temps, je recommanderais simplement de redémarrer l'application si son code change. Vous vous donnerez probablement juste des maux de tête.

0
Glenn Maynard