53 votes

Pourquoi les méthodes de ramassage des ordures de Java et de Python sont-elles différentes ?

Python utilise la méthode de comptage des références pour gérer la durée de vie des objets. Ainsi, un objet qui n'a plus d'utilité sera immédiatement détruit.

Mais, en Java, le GC (garbage collector) détruit les objets qui ne sont plus utilisés à un moment précis.

Pourquoi Java choisit-il cette stratégie et quel en est l'avantage ?

Est-ce mieux que l'approche Python ?

47voto

Daren Thomas Points 26812

L'utilisation du comptage de référence présente des inconvénients. L'un des plus mentionnés est celui des références circulaires : Supposons que A fait référence à B, que B fait référence à C et que C fait référence à B. Si A supprime sa référence à B, B et C auront toujours un compte de référence de 1 et ne seront pas supprimés avec le comptage de référence traditionnel. CPython (le comptage de références ne fait pas partie de python lui-même, mais de son implémentation en C) attrape les références circulaires avec une routine de collecte de déchets séparée qu'il exécute périodiquement...

Un autre inconvénient : Le comptage de références peut rendre l'exécution plus lente. À chaque fois qu'un objet est référencé et déréférencé, l'interprète/VM doit vérifier si le compte est passé à 0 (puis désallouer si c'est le cas). Le Garbage Collection n'a pas besoin de faire cela.

De même, la collecte des déchets peut être effectuée dans un thread séparé (bien que cela puisse être un peu délicat). Sur les machines disposant de beaucoup de RAM et pour les processus qui n'utilisent la mémoire que lentement, il est préférable de ne pas faire de GC du tout ! Le comptage de références serait un peu un inconvénient en termes de performances...

28voto

Luke Quinane Points 8257

En fait, le comptage de références et les stratégies utilisées par la JVM de Sun sont tous des types différents d'algorithmes de collecte des déchets.

Il existe deux grandes approches pour retrouver les objets morts : le traçage et le comptage de références. Dans le traçage, la GC commence par les "racines" - des choses comme les références de la pile, et trace tous les objets atteignables (vivants). Tout ce qui ne peut pas être atteint est considéré comme mort. Dans le comptage de référence, chaque fois qu'une référence est modifiée, les objets concernés voient leur compte mis à jour. Tout objet dont le compte de référence est mis à zéro est considéré comme mort.

Avec pratiquement toutes les implémentations GC, il y a des compromis à faire, mais le traçage est généralement bon pour les opérations à haut débit (c'est-à-dire rapides), mais il a des temps de pause plus longs (de plus grands intervalles où l'interface utilisateur ou le programme peut se figer). Le comptage de référence peut fonctionner en plus petits morceaux mais sera globalement plus lent. Cela peut signifier moins de blocages, mais une performance globale plus faible.

De plus, un GC à comptage de références nécessite un détecteur de cycle pour nettoyer tous les objets d'un cycle qui ne seront pas détectés par leur seul comptage de références. Perl 5 n'avait pas de détecteur de cycle dans son implémentation GC et pouvait laisser fuir de la mémoire cyclique.

Des recherches ont également été menées pour obtenir le meilleur des deux mondes (faibles temps de pause, débit élevé) : http://cs.anu.edu.au/~Steve.Blackburn/pubs/papers/urc-oopsla-2003.pdf

15voto

Eli Courtwright Points 53071

Darren Thomas donne une bonne réponse. Cependant, une grande différence entre les approches Java et Python est qu'avec le comptage de références dans le cas commun (pas de références circulaires), les objets sont nettoyés immédiatement plutôt qu'à une date ultérieure indéterminée.

Par exemple, je peux écrire du code bâclé et non portable en CPython, tel que

def parse_some_attrs(fname):
    return open(fname).read().split("~~~")[2:4]

et le descripteur de fichier pour le fichier que j'ai ouvert sera nettoyé immédiatement car dès que la référence au fichier ouvert disparaît, le fichier est ramassé et le descripteur de fichier est libéré. Bien sûr, si j'exécute Jython ou IronPython ou éventuellement PyPy, le ramasseur d'ordures ne sera pas nécessairement lancé avant beaucoup plus tard ; il est possible que je sois à court de descripteurs de fichiers avant et que mon programme plante.

Donc vous DEVRIEZ écrire du code qui ressemble à

def parse_some_attrs(fname):
    with open(fname) as f:
        return f.read().split("~~~")[2:4]

mais les gens aiment parfois compter sur le comptage de références pour toujours libérer leurs ressources, car cela peut parfois rendre votre code un peu plus court.

Je dirais que le meilleur ramasseur de déchets est celui qui offre les meilleures performances, ce qui semble être actuellement les ramasseurs de déchets générationnels de style Java qui peuvent s'exécuter dans un thread séparé et ont toutes ces optimisations folles, etc. Les différences dans la façon dont vous écrivez votre code devraient être négligeables et idéalement inexistantes.

8voto

Espo Points 24318

Je pense que l'article " Théorie et pratique de Java : Une brève histoire du ramassage des ordures " d'IBM devrait aider à expliquer certaines des questions que vous vous posez.

5voto

mfx Points 4517

Le ramassage des déchets est plus rapide que le comptage des références, si la mémoire est suffisante. Par exemple, un gc de copie parcourt les objets "vivants" et les copie dans un nouvel espace, et peut récupérer tous les objets "morts" en une seule étape en marquant une région mémoire entière. C'est très efficace, si vous avez assez de mémoire. Les collections générationnelles utilisent le fait que "la plupart des objets meurent jeunes" ; souvent, seuls quelques pour cent des objets doivent être copiés.

[C'est aussi la raison pour laquelle gc peut être plus rapide que malloc/free].

Le comptage de références est beaucoup plus économe en espace que le ramassage des déchets, car il récupère la mémoire au moment même où elle devient inaccessible. Ceci est intéressant lorsque vous voulez attacher des finaliseurs à des objets (par exemple pour fermer un fichier lorsque l'objet File devient inaccessible). Un système de comptage de références peut fonctionner même lorsque seulement quelques pour cent de la mémoire est libre. Mais le coût de gestion de l'incrémentation et de la décrémentation des compteurs à chaque assignation de pointeur coûte beaucoup de temps, et une certaine forme de garbage collection est toujours nécessaire pour récupérer des cycles.

Le compromis est donc clair : si vous devez travailler dans un environnement où la mémoire est limitée, ou si vous avez besoin de finaliseurs précis, utilisez le comptage de références. Si vous disposez de suffisamment de mémoire et avez besoin de rapidité, utilisez le garbage collection.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X