Как я могу отладить возможную утечку памяти в скрипте Perl CGI? - PullRequest
2 голосов
/ 28 января 2010

У меня есть устаревшая CGI-страница Perl, работающая на Apache, которая обрабатывает большие данные таблицы Excel, добавляя их в базу данных по мере необходимости. Он обрабатывается группами данных, а каждая группа данных отправляется в базу данных.

После каждого обращения к базе данных доступная память моей системы значительно уменьшается до точки, в которой не осталось памяти. Когда я наконец получаю сообщение об ошибке «Преждевременный конец заголовка сценария» и HTTP-код 500 возвращается клиенту, память освобождается обратно в систему.

Просматривая (сложный) код, я не могу найти, где может происходить утечка памяти. Есть какой-то трюк или инструмент, который я могу использовать, чтобы определить, куда идет память?

Ответы [ 3 ]

2 голосов
/ 29 января 2010

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

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

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

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

Если это не проблема с веб-сервером, начните разбивать скрипт на части, как если бы вы столкнулись с любой проблемой отладки. Если у вас включено ведение журнала, включите его и наблюдайте за запуском программы во время записи ее реального использования памяти. Когда он взорвется? Похоже, вы сузили его до некоторых вызовов базы данных. Если вы сможете запустить это из командной строки или отладчика, я найду пару подходящих точек останова до и после увеличения памяти и постепенно сближу их. Вы можете использовать такие модули, как Devel :: Size , чтобы посмотреть на размеры памяти для структур данных, которые вы подозреваете.

Оттуда это только сужает подозреваемых. Когда вы найдете подозреваемого, посмотрите, сможете ли вы воспроизвести его в коротком примере сценария. Вы хотите исключить максимально возможное количество факторов, способствующих этому.

Как только вы думаете, что нашли код, вызывающий нарушение, возможно, вы можете задать другой вопрос, который показывает код, если вы все еще не понимаете, что происходит.

Если вы хотите по-настоящему увлекаться, вы можете написать свой собственный отладчик Perl. Это не так сложно. Вы получаете возможность запустить некоторые подпрограммы в пространстве имен DB в начале или конце операторов. У вас есть свои профили памяти списка отладочных кодов для того, что вы подозреваете, и ищите скачки в размерах памяти. Я не попробовал бы это, если бы все остальное не терпело неудачу.

0 голосов
/ 29 января 2010

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

Что попробовать:

  • Очистить переменные объекта, когда закончите с ними
  • Выполните глубокое циклическое подключение к базе данных (это экстремально, но в зависимости от того, как вы подключаетесь, это может решить проблему).
  • Загружать только одну группу данных за раз и очищать любые переменные или объекты фабрики, загружающие их между группами.

Надеюсь, это поможет некоторым.

0 голосов
/ 29 января 2010

Если проблема в коде Perl, у вас может быть ссылка, указывающая на себя или на родительский узел.

Вот краткий пример кода, демонстрирующего такое поведение.

{
  my @a;
  @a = [\@a];
}

Обычно это происходит в форме объекта, который ссылается на родительский объект.

{ package parent;
  sub new{ bless { 'name' => $_[1] }, $_[0] }
  sub add_child{
    my($self,$child_name) = @_;
    my $child = child->new($child_name,$self);
    $self->{$child_name} = $child;   # saves a reference to the child
    return $child;
  }
}
{ package child;
  sub new{
    my($class,$name,$parent) = @_;
    my $self = bless {
      'name' => $name,
      'parent' => $parent # saves a reference to the parent
    }, $class;
    return $self;
  }
}
{
  my $parent = parent->new('Dad');
  my $child  = parent->add_child('Son');

  # At this point both of these are true
  # $parent->{Son}{parent} == $parent
  # $child->{parent}{Son}  == $child

  # Both of the objects **would** be destroyed upon leaving
  # the current scope, except that the object is self-referential
}

# Both objects still exist here, but there is no way to access either of them.

Лучший способ исправить это - использовать Scalar :: Util :: weaken .

use Scalar::Util qw'weaken';
{ package child;
  sub new{
    my($class,$name,$parent) = @_;
    my $self = bless {
      'name' => $name,
      'parent' => $parent
    }, $class;

    weaken ${$self->{parent}};

    return $self;
  }
}

Я бы порекомендовал удалить ссылку на родительский объект из дочернего объекта, если это вообще возможно.

...