33 votes

Profilage de l'utilisation de la mémoire en Perl et détection des fuites ?

J'ai écrit un service réseau persistant en Perl qui fonctionne sous Linux.

Malheureusement, au fur et à mesure qu'il fonctionne, sa taille de pile résidente (RSS) augmente, et augmente, et augmente, lentement mais sûrement.

Et ce, malgré les efforts diligents que j'ai déployés pour supprimer toutes les clés de hachage inutiles et toutes les références aux objets qui, autrement, feraient en sorte que les comptes de référence restent en place et entravent le ramassage des ordures.

Existe-t-il de bons outils pour profiler l'utilisation de la mémoire associée aux diverses primitives de données natives, aux objets de référence de hachage bénis, etc. dans un programme Perl ? Qu'utilisez-vous pour traquer les fuites de mémoire ?

Je n'ai pas l'habitude de passer du temps dans le débogueur Perl ou dans l'un des divers profileurs interactifs, donc une réponse chaleureuse, douce et non ésotérique serait appréciée :-).

Merci d'avance !

14voto

Ether Points 34103

Vous pouvez avoir une référence circulaire dans l'un de vos objets. Lorsque le ramasseur d'ordures vient désallouer cet objet, la référence circulaire signifie que tout ce qui est référencé par cette référence ne sera jamais libéré. Vous pouvez vérifier la présence de références circulaires avec Devel::Cycle et Test::Memory::Cycle . Une chose à essayer (bien que cela puisse devenir coûteux dans le code de production, je le désactiverais donc lorsqu'un drapeau de débogage n'est pas activé) est de vérifier les références circulaires dans le destructeur de tous vos objets :

# make this be the parent class for all objects you want to check;
# or alternatively, stuff this into the UNIVERSAL class's destructor
package My::Parent;
use strict;
use warnings;
use Devel::Cycle;   # exports find_cycle() by default

sub DESTROY
{
    my $this = shift;

    # callback will be called for every cycle found
    find_cycle($this, sub {
            my $path = shift;
            foreach (@$path)
            {
                my ($type,$index,$ref,$value) = @$_;
                print STDERR "Circular reference found while destroying object of type " .
                    ref($this) . "! reftype: $type\n";
                # print other diagnostics if needed; see docs for find_cycle()
            }
        });

    # perhaps add code to weaken any circular references found,
    # so that destructor can Do The Right Thing
}

10voto

Ether Points 34103

Vous pouvez utiliser Devel::Leak pour rechercher les fuites de mémoire. Cependant, la documentation est plutôt maigre... par exemple, où trouver la référence $handle à transmettre à Devel::Leak::NoteSV() ? Si je trouve la réponse, je modifierai cette réponse.

Ok, il s'avère que l'utilisation de ce module est assez simple (code volé sans vergogne à partir de Apache::Leak ) :

my $handle; # apparently this doesn't need to be anything at all
my $leaveCount = 0;
my $enterCount = Devel::Leak::NoteSV($handle);
print STDERR "ENTER: $enterCount SVs\n";

#  ... code that may leak

$leaveCount = Devel::Leak::CheckSV($handle);
print STDERR "\nLEAVE: $leaveCount SVs\n";

Je placerais autant de code que possible dans la section du milieu, avec le contrôle leaveCount aussi près que possible de la fin de l'exécution (si vous en avez une) - après que la plupart des variables aient été désallouées (si vous ne pouvez pas faire sortir une variable de sa portée, vous pouvez lui assigner undef pour libérer ce vers quoi elle pointait).

4voto

Ether Points 34103

Ce qu'il faut essayer ensuite (je ne suis pas sûr que cela soit mieux placé dans un commentaire après la question d'Alex ci-dessus) : Ce que j'essaierais ensuite (autre que Devel::Leak) :

Essayez d'éliminer les parties "inutiles" de votre programme, ou segmentez-le en exécutables séparés (ils pourraient utiliser des signaux pour communiquer, ou s'appeler mutuellement avec des arguments de ligne de commande peut-être) -- le but est de réduire un exécutable à la plus petite quantité de code qui présente encore le mauvais comportement. . Si vous êtes sûr que ce n'est pas votre code qui est en cause, réduisez le nombre de modules externes que vous utilisez, en particulier ceux qui ont une implémentation XS. Si c'est peut-être votre propre code, cherchez tout ce qui pourrait être suspect :

  • définitivement toute utilisation de code Inline::C ou XS
  • utilisation directe de références, par exemple \@list ou \%hash plutôt que des références préallouées comme [ qw(foo bar) ] (la première crée une autre référence qui peut se perdre ; dans la seconde, il n'y a qu'une seule référence à prendre en compte, qui est généralement stockée dans un scalaire lexical local).
  • manipuler les variables de manière indirecte, par exemple $$foo$foo est modifié, ce qui peut provoquer l'autoviviation des variables (bien que vous deviez désactiver l'option strict 'refs' vérification)

3voto

Cagatay Points 949

J'ai récemment utilisé NYTProf comme profileur pour une grosse application Perl. Il ne suit pas l'utilisation de la mémoire, mais il trace tous les chemins de code exécutés, ce qui aide à trouver l'origine des fuites. Si les fuites concernent des ressources rares, telles que des connexions à des bases de données, le suivi de l'endroit où elles sont allouées et fermées contribue grandement à la détection des fuites.

2voto

Steve Schnepp Points 2490

Un bon guide à ce sujet est inclus dans le manuel Perl : Déboguer l'utilisation de la mémoire de Perl

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