9
votes

Django Python Garbage Collection Woes

Après 2 jours de débogage, j'ai cloué mon temps de porc: le collecteur des ordures Python.
Mon application contient beaucoup d'objets en mémoire. Et ça marche bien.
Le GC fait les tours habituels (je n'ai pas joué avec les seuils par défaut de (700, 10, 10)).
Une fois de temps en temps, au milieu d'une transaction importante, le balayage de la 2e génération frappe et passe en revue mes objectifs de génération de 1,5 m.
cela prend 2 secondes! La transaction nominale prend moins de 0,1 seconde.

Ma question est que dois-je faire?
Je peux désactiver la génération 2 balayages (en définissant un seuil très élevé - est-ce la bonne voie?) Et le GC est obéissant.
Quand devrais-je les allumer?
Nous avons mis en place un service Web utilisant Django et chaque demande d'utilisateur prend environ 0,1 seconde.
De manière optimale, je vais exécuter ces cycles GC Gen 2 entre les demandes d'API d'utilisateur . Mais comment puis-je faire ça?
Ma vue se termine par retour httpreesponse () , après que j'aimerais exécuter un balayage Gen 2 gc.
Comment je fais ça? Cette approche a-t-elle un sens?

Puis-je marquer l'objet qui n'a jamais besoin d'être des ordures collectées, de sorte que le GC ne les testera pas tous les 2e cycle général?
Comment puis-je configurer le GC pour exécuter des balayages complets lorsque le serveur Django est relativement inactif?

python 2.6.6 sur plusieurs plates-formes (Windows / Linux).


6 commentaires

"Mon application tient beaucoup d'objets en mémoire"? Comment?


Les conteneurs sont des dictionnaires standard. Les objets eux-mêmes sont soit mes propres instances de classe (dérivés d'objet), soit des tuples, dans lequel l'un des éléments est une référence auxdites instances de classe (et le reste des éléments sont des INTS).


Puisque Django Demande et Répondre Les objets sont transitoires, comment pouvez-vous vous tenir quelque chose en mémoire?


@ S.LOTT: E.G. en mettant le dictionnaire dans l'espace de noms d'un module. Tout ne doit pas vivre dans le cycle de demande / réponse.


@piquadrat: correct. Il y a d'autres moyens, aussi. L'utilisation de modules au lieu de sessions peut être appropriée à cette question ou non. Il est important de savoir précisément ce qui se passe plutôt que de deviner. Je ne suis pas facilement convaincu que c'est le GC sans plus de preuves.


@piquadrat est correct. J'ai utilisé un objet global dans l'espace de noms d'un module. Cet objet, en plus d'être énorme, prend également beaucoup de temps pour initialiser, c'est pourquoi je l'ai rendue globale en premier lieu. BTW - c'est aussi constant.


5 Réponses :


3
votes

Je crois qu'une option serait de désactiver complètement la collection de déchets, puis de collecter manuellement à la fin d'une demande, comme suggéré ici: Comment le mécanisme de collecte des ordures fonctionne-t-il?

J'imagine que vous pouvez désactiver le fichier GC dans votre Params.py.py code>. P>

Si vous souhaitez exécuter Garbagecollection Sur chaque demande, je suggérerais de développer certains middleware qui le fait dans le Réponse de processus Méthode: P>

import gc
class GCMiddleware(object):
    def process_response(self, request, response):
        gc.collect()
        return response


2 commentaires

Bien que je n'ai pas encore implémenté cela, cela ressemble à la bonne approche.


Non ce n'est pas la bonne approche, il recueille GC avant de retourner la réponse. Il va toujours bloquer le retour de la réponse.



0
votes

Ma vue se termine par le retour httpresponse (), après quoi j'aimerais exécuter un balayage Gen 2 GC. P>

// turn off GC
// do stuff
resp = HttpResponse()
// turn on GC
return resp

0 commentaires

1
votes

Une alternative peut être de désactiver GC tout à fait et configurez mod_wsgi (ou tout ce que vous utilisez) pour tuer et redémarrer des processus plus fréquemment.


0 commentaires

6
votes

Nous avons fait quelque chose comme ça pour Gunicorn. En fonction de ce que vous utilisez le serveur WSGI, vous devez trouver les hameçons de droite pour après la réponse, non auparavant. Django a un signe request_forme_fined CODE> SIGNAL mais ce signal est toujours pré-réponse.

pour GunCorn, dans la configuration, vous devez définir 2 méthodes, telles que: P>

def pre_request(worker, req):
    # disable gc until end of request
    gc.disable()


def post_request(worker, req, environ, resp):
    # enable gc after a request
    gc.enable()


0 commentaires

0
votes

bâtiment sur l'approche de @milkypostman, vous pouvez utiliser Gevent. Vous voulez un appel à la collection de déchets par demande em> mais le problème de la suggestion @milkypostman est que l'appel à GC.Collect () bloquera toujours le retour de la demande. Gevent nous permet de revenir immédiatement et de faire passer le GC Exécuter après le renvoi de em> le processus est renvoyé.

premier dans votre fichier WSGI Assurez-vous du patch de singe avec gevent magique et désactive la collection de déchets. Vous pouvez définir gc.disable () code> mais certaines bibliothèques ont des gestionnaires de contexte qui l'allument après l'avoir désactivé (MessagePack par exemple), le seuil 0 est plus collant. P>

from gc import collect
import gevent

class BaseMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response


class GcCollectMiddleware(BaseMiddleware):
    """Middleware which performs a non-blocking gc.collect()"""

    def __call__(self, request):
        response = self.get_response(request)
        gevent.spawn(collect)
        return response


1 commentaires

Je devrais également noter que lors de l'ajout du middleware à votre fichier de paramètres, assurez-vous que c'est le dernier middleware.