Циркулярные ссылки являются наиболее распространенной канонической причиной утечек.
sub leak {
my ($foo, $bar);
$foo = \$bar;
$bar = \$foo;
}
Perl использует сборщик мусора для подсчета ссылок. Это означает, что Perl ведет подсчет того, какие указатели на любую переменную существуют в данный момент времени. Если переменная выходит из области видимости и число равно 0, переменная очищается.
В приведенном выше примере кода $foo
и $bar
никогда не собираются, и копия сохраняется после каждого вызова leak()
, поскольку обе переменные имеют счетчик ссылок 1.
Самый простой способ предотвратить эту проблему - использовать слабые ссылки. Слабые ссылки - это ссылки, которые вы используете для доступа к данным, но не учитываются при сборке мусора.
use Scalar::Util qw(weaken);
sub dont_leak {
my ($foo, $bar);
$foo = \$bar;
$bar = \$foo;
weaken $bar;
}
В dont_leak()
, $foo
имеет счетчик ссылок 0, $bar
имеет счетчик ссылок 1. Когда мы покидаем область действия подпрограммы, $foo
возвращается в пул, и его ссылка на $bar
очищается. Это уменьшает количество ссылок на $bar
до 0, что означает, что $bar
также может вернуться в пул.
Обновление:
Мозг Фой спросил, есть ли у меня какие-либо данные, подтверждающие мое утверждение о том, что циклические ссылки распространены. Нет, у меня нет статистики, чтобы показать, что циклические ссылки распространены. Они являются наиболее часто обсуждаемой и лучше всего документированной формой утечек памяти perl.
Мой опыт показывает, что они случаются. Вот краткое изложение утечек памяти, которые я видел за десятилетие работы с Perl.
У меня были проблемы с утечкой приложений pTk. Некоторые утечки, которые я смог доказать, произошли из-за циклических ссылок, которые возникли, когда Tk передает ссылки на окна. Я также видел утечки pTk, причину которых я никогда не мог отследить.
Я видел, как люди неправильно понимают weaken
и случайно попадают с круговыми ссылками.
Я видел непреднамеренные циклы, возникающие, когда слишком много плохо продуманных объектов собираются вместе в спешке.
Однажды я обнаружил утечки памяти, вызванные модулем XS, который создавал большие, глубокие структуры данных. Мне никогда не удавалось получить воспроизводимый контрольный пример, который был бы меньше, чем вся программа. Но когда я заменил модуль другим сериализатором, утечки исчезли. Итак, я знаю, что эти утечки произошли из XS.
Итак, по моему опыту, циклы являются основным источником утечек.
К счастью, есть модуль , который поможет отследить их.
Относительно того, являются ли крупные глобальные структуры, которые никогда не очищаются, "утечками", я согласен с Брайаном. Они крякают как утечки (у нас постоянно растет использование памяти процесса из-за ошибки), поэтому они утечки. Несмотря на это, я не помню, чтобы когда-либо видел эту конкретную проблему в дикой природе.
Основываясь на том, что я вижу на сайте Стоунхенджа, я предполагаю, что Брайан видит много больного кода от людей, для которых он тренирует или готовит лечебные чудеса. Так что его набор сэмплов намного больше и разнообразнее, чем мой, но у него есть свой выбор.
Какая причина утечек является наиболее распространенной? Я не думаю, что мы когда-либо действительно узнаем. Но мы все можем согласиться с тем, что циклические ссылки и хранилища глобальных данных - это анти-паттерны, которые должны быть устранены, где это возможно, и обрабатываться с осторожностью и осторожностью в тех немногих случаях, когда они имеют смысл.