Профилирование использования памяти Perl и обнаружение утечек?

Я написал постоянную сетевую службу в Perl, которая работает на Linux.

к сожалению, как он работает, его резидентный размер стека (RSS) просто растет, и растет, и растет, медленно, но верно.

это несмотря на усердные усилия с моей стороны, чтобы удалить все ненужные хэш-ключи и удалить все ссылки на объекты, которые в противном случае заставили бы счетчик ссылок оставаться на месте и препятствовать сбору мусора.

есть ли хорошие инструменты для профилирования использование памяти, связанное с различными примитивами собственных данных, благословенными ссылочными объектами хэша и т. д. в рамках программы Perl? Что вы используете для отслеживания утечек памяти?

Я обычно не провожу время в отладчике Perl или любом из различных интерактивных профилировщиков, поэтому теплый, нежный, неэзотерический ответ будет оценен. :-)

5 ответов


у вас может быть круговая ссылка в одном из ваших объектов. Когда сборщик мусора приходит, чтобы освободить этот объект, круговая ссылка означает, что все, на что ссылается эта ссылка, никогда не освободится. Вы можете проверить наличие круговых ссылок с помощью Devel:: Cycle и Тест::Память::Цикл. Одна вещь, которую нужно попробовать (хотя это может стоить дорого в производственном коде, поэтому я бы отключил его, когда флаг отладки не установлен), проверяет циркуляр ссылки внутри деструктора для всех ваших объектов:

# 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
}

можно использовать В Devel::Утечка для поиска утечек памяти. Однако, документация довольно скудная... например, где можно получить ссылку $handle для передачи в Devel::Leak::NoteSV()? если я найду ответ, я отредактирую этот ответ.

Ok оказывается, что использование этого модуля довольно просто (код бесстыдно украден из Apache:: Утечка):

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";

Я бы разместить как можно больше кода в средней части, с проверкой leaveCount как можно ближе к концу выполнения (если у вас есть), насколько это возможно-после того, как большинство переменных были освобождены (если вы не можете получить переменную из области действия, вы можете назначить undef ей, чтобы освободить то, на что она указывала).


что дальше попробовать (не уверен, что это будет лучше всего помещено в комментарий после вопроса Алекса выше): что я попробую дальше (кроме Devel:: Leak):

попробуйте устранить "ненужные" части вашей программы или сегментировать ее на отдельные исполняемые файлы (они могут использовать сигналы для связи или вызывать друг друга с аргументами командной строки)-цель состоит в том, чтобы кипятить исполняемый файл в наименьшее количество кода, который по-прежнему демонстрирует плохое поведение. Если вы уверены, что это не ваш код, уменьшите количество внешних модулей, которые вы используете, особенно те, которые имеют реализацию XS. Если, возможно, это ваш собственный код, ищите что-нибудь потенциально подозрительное:

  • определенно любое использование Inline:: C или XS code
  • прямые ссылки, например \@list или \%hash, а не предварительно выделенные ссылки, такие как [ qw (foo bar) ] (первый создает другую ссылку, которая может потеряться; в последнем, существует только одна ссылка для беспокойства, которая обычно хранится в локальном лексическом скаляре
  • косвенное управление переменными, например $$foo здесь $foo изменяется, что может вызвать автовификацию переменных (хотя вам нужно отключить strict 'refs' проверка)

Я недавно использовал NYTProf в качестве профилировщика для большого приложения Perl. Он не отслеживает использование памяти, но отслеживает все выполненные пути кода, что помогает выяснить, где возникают утечки. Если вы просачиваете скудные ресурсы, такие как подключения к базе данных, трассировка, где они выделены и закрыты, проходит долгий путь к поиску утечек.


хорошее руководство об этом включено в руководство Perl:отладка использования памяти Perl