2009-08-31 7 views
33

J'ai écrit un service réseau persistant dans Perl qui tourne sous Linux. Malheureusement, en cours d'exécution, sa taille de pile résidente (RSS) ne cesse de croître, de croître et de se développer, lentement mais sûrement.Profilage d'utilisation de la mémoire Perl et détection de fuite?

Ceci en dépit des efforts diligents de ma part pour effacer toutes les clés de hachage inutiles et supprimer toutes les références à des objets qui, autrement, provoqueraient le maintien des compteurs de référence et empêcheraient la récupération de place.

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

Je ne passe habituellement pas de temps dans le débogueur Perl ou l'un des différents profileurs interactifs, donc une réponse chaleureuse, douce et non ésotérique serait appréciée. :-)

+0

L'avez-vous découvert? Ma meilleure supposition étant donné l'information que vous avez fournie est qu'il y a une bibliothèque (fournie par le dynaloader d'un module) qui est le coupable ... – Ether

+0

Cela semble être devenu la question canonique "trouver une fuite de mémoire", puisque mes réponses d'autres des questions similaires ont toutes été fusionnées ici :) Je n'ai pas répondu à une question trois fois; plusieurs threads ont été fusionnés ensemble au fil du temps. – Ether

+0

Glissement de langue ... vous vouliez dire "Resident Set Size" ... ce nombre est sans rapport avec la pile –

Répondre

13

Vous pourriez avoir une référence circulaire dans l'un de vos objets. Lorsque le garbage collector arrive pour libérer 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 les références circulaires avec Devel::Cycle et Test::Memory::Cycle. Une chose à essayer (bien que cela puisse coûter cher dans le code de production, donc je le désactiver quand un indicateur de débogage n'est pas défini) est la vérification des références circulaires à l'intérieur du destructor pour 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 
} 
+1

PS. Vous pouvez affaiblir une référence existante (pour permettre à un destructeur de travailler sa magie à travers des cycles) avec Scalar :: Util :: weaken() - http://search.cpan.org/~gbarr/Scalar-List-Utils-1.21 /lib/Scalar/Util.pm – Ether

+2

Hi Ether - essayé UNIVERSAL :: DESTROY(), a longtemps fonctionné le service et frappé dessus, et n'a rien obtenu. Pendant ce temps, RSS rampait. Quoi d'autre pourrait-il être s'il ne s'agissait pas de références circulaires? –

+2

Je ne suis pas vraiment sûr que la recherche de fuites de mémoire dans un destructeur va vous faire des faveurs. Si vous fuyez, DESTROY ne sera jamais appelé. –

9

Vous pouvez Utilisez pour rechercher des fuites de mémoire. Cependant, la documentation est assez éparse ... par exemple, où obtient-on la référence $ handle à passer à Devel::Leak::NoteSV()? f Je trouve la réponse, je vais éditer cette réponse.

Ok, il se révèle que l'utilisation de ce module est assez simple (code volé sans vergogne de Apache::Leak):

use Devel::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 placer le code autant que possible dans la partie centrale, avec le contrôle de leaveCount comme proche de la fin de l'exécution (si vous en avez une) - après que la plupart des variables ont été libérées autant que possible (si vous ne pouvez pas obtenir une variable hors portée, vous pouvez lui attribuer undef pour libérer tout ce qu'il pointait vers).

+0

les variables sont utilisées de manière incohérente dans l'exemple, par ex. $ leave vs $ leaveCount – Lot105

4

Et ensuite d'essayer (ne sais pas si ce serait le mieux placé dans un commentaire après la question de Alex ci-dessus bien): Ce que je vais essayer à côté (autre que Devel :: fuite):

Essayez d'éliminer " inutiles "parties de votre programme, ou le segmenter en exécutables distincts (ils pourraient utiliser des signaux pour communiquer, ou s'appeler entre eux avec des arguments de ligne de commande peut-être) - le but est réduire un exécutable dans la plus petite quantité de code présente toujours le mauvais comportement. Si vous êtes sûr que ce n'est pas votre code, réduisez le nombre de modules externes que vous utilisez, en particulier ceux qui ont une implémentation XS. Si peut-être il est votre propre code, chercher quelque chose potentiellement louche:

  • certainement toute utilisation de Inline :: C ou code 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)] (le premier crée une autre référence qui peut être perdue, dans le second, il n'y a qu'une référence à s'inquiéter, qui est généralement stockée dans un scalaire lexical local
  • manipulant des variables indirectement, par exemple $$foo$foo est modifiée, ce qui peut provoquer autovivication des variables (bien que vous devez désactiver strict 'refs' vérifier) ​​
2

J'ai récemment utilisé NYTProf en tant que profileur pour une grande 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 permet de savoir d'où proviennent les fuites. Si ce que vous fuyez, ce sont des ressources rares telles que les connexions à la base de données, le repérage où elles sont attribuées et fermées est un bon moyen de détecter les fuites.