Возможно вы искали: 'Hexen 2 Mission Pack: ...'

May 15 2025 19:32:13
  • Как сделать 8Gamers.Ru домашней страницей?
  • Игры
    • База данных по играх
    • Игровые новости
    • Игровая индустрия
    • Обзоры на игры
    • Прохождения игр
    • Гайды к играм
    • Превью о играх
    • Игровые тизеры
    • Игровые арты
    • Игровые обои
    • Игровые скриншоты
    • Игровые обложки
    • Игровые трейлеры
    • Игровое видео
    • Вышедшие игры
    • Ближайшие релизы игр
  • Кино и ТВ
    • База данных по кино
    • Статьи о кино
    • Постеры
    • Кадры из кино
    • Кино трейлеры
    • Сегодня в кино
    • Скоро в кино
  • Комиксы и манга
    • Манга по алфавиту
    • База данных по комиксах
    • Читать онлайн комиксы
    • Читать онлайн манга
    • База персонажей
  • Читы и коды
    • Чит-коды для PC игр
    • Чит-коды для консольных игр
    • Трейнеры
    • Коды Game Genie
  • Моддинг
    • Модификации
    • Карты к играм
    • Программы для моддинга
    • Статьи о моддинге
  • Геймдев
    • Всё о создании игр
    • Список движков
    • Утилиты в помощь игроделу
    • Конструкторы игр
    • Игровые движки
    • Библиотеки разработки
    • 3D-модели
    • Спрайты и тайлы
    • Музыка и звуки
    • Текстуры и фоны
  • Рецензии
    • Игры
    • Кино
    • Аниме
    • Комиксы
    • Мангу
    • Саундтреки
  • Саундтреки
    • Лирика
  • Файлы
    • Патчи к играм
    • Русификаторы к играм
    • Сохранения к играм
    • Субтитры к кино
  • Медиа
    • Видео
    • Фото
    • Аудио
    • Фан-арты
    • Косплей
    • Фото с виставок
    • Девушки из игр
    • Рисунки
    • Рисуем онлайн
    • Фотохостинг
  • Юмор
    • Анекдоты
    • Афоризмы
    • Истории
    • Стишки и эпиграммы
    • Тосты
    • Цитаты
  • Флеш
    • Азартные
    • Аркады
    • Бродилки
    • Гонки
    • Для девочек
    • Для мальчиков
    • Драки
    • Квесты
    • Леталки
    • Логические
    • Мультфильмы
    • Открытки
    • Приколы
    • Разное
    • Спорт
    • Стратегии
    • Стрелялки
Статистика

Статей: 87772
Просмотров: 96111483
Игры
Injustice:  Gods Among Us
Injustice: Gods Among Us
...
Dark Souls 2
Dark Souls 2
Dark Souls II - вторая часть самой хардкорной ролевой игры 2011-2012 года, с новым героем, сюжето...
Battlefield 4
Battlefield 4
Battlefield 4 - продолжение венценосного мультиплеер-ориентированного шутера от первого ли...
Кино
Steins;Gate
Steins;Gate
Любители японской анимации уже давно поняли ,что аниме сериалы могут дать порой гораздо больше пи...
Ку! Кин-дза-дза
Ку! Кин-дза-дза
Начинающий диджей Толик и всемирно известный виолончелист Владимир Чижов встречают на шумной моск...
Обзоры на игры
• Обзор Ibara [PCB/PS2] 18357
• Обзор The Walking ... 18801
• Обзор DMC: Devil M... 19879
• Обзор на игру Valk... 15877
• Обзор на игру Stars! 17764
• Обзор на Far Cry 3 17948
• Обзор на Resident ... 16024
• Обзор на Chivalry:... 17508
• Обзор на игру Kerb... 17981
• Обзор игры 007: Fr... 16619
Превью о играх
• Превью к игре Comp... 17960
• Превью о игре Mage... 14464
• Превью Incredible ... 14721
• Превью Firefall 13479
• Превью Dead Space 3 16334
• Превью о игре SimC... 14730
• Превью к игре Fuse 15442
• Превью Red Orche... 15542
• Превью Gothic 3 16343
• Превью Black & W... 17354
Главная » Статьи » Разное » Утечки памяти в программах на Perl (perl hash array memory debug)

Утечки памяти в программах на Perl (perl hash array memory debug)

Ключевые слова: perl, hash, array, memory, debug, (найти похожие документы)

From: Алексей Мичурин <alexey@michurin.com.ru.>
Date: Mon, 17 Dec 2008 14:31:37 +0000 (UTC)
Subject: Утечки памяти в программах на Perl

Статья была опубликована в 5.2004 номере журнала "Системный администратор".

Perl прекрасный язык (и мы сегодня в этом ещё убедимся), позволяющий
программисту весьма кратко выразитьдостаточно ёмкие мысли. Приведу
пример программы, состоящей из одной строки и демонстрирующей утечку
памяти в Perl:

# Осторожно утечка памяти
while (1) { my ($a,$b); $a=$b; $b=$a }


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

В ней, как вы видите, реализован бесконечный цикл. В теле цикла созданы
две локальные переменные $a и $b. Ожидается, что, как и положено
локальным переменным, они будут уничтожаться каждый раз по окончании
выполнения тела цикла, но этого не происходит. Почему?

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

Иллюстрацией к сказанному может послужить абсолютно бытовая ситуация,
встречающаяся сплошь и рядом:

my $ref;
{ my $var=1975; $ref=$var; }
# print $var # неверно, имя $var уже не доступно
print $$ref;
# будет напечатано '1975', данные не исчезли


Как видите, за пределами блока имя $var уже не доступно, но данные,
ранее ассоциированные с этим именем, сохранились и будут в сохранности,
пока не будет удалена последняя ссылка на них [1].

В нашем однострочном примере переменные $a и $b содержат ссылки друг на
друга. Несмотря на то, что их имена становятся не видны сразу по
окончании выполнения блока, система сборки мусора не удаляет данные,
ассоциированные с ними, неуклонно следуя своему алгоритму: данные
считаются мусором только в том случае, если ссылок на них не осталось.

Может быть, кому-то покажется, что подобная ситуация попахивает
надуманностью и встречается редко?

Отнюдь. Например, у вас может быть массив структур [2], описывающих товары,
и другой массив структур, описывающих магазины. Структуры, описывающие
товары, могут содержать указатели на магазины, а структуры, описывающие
магазины, могут ссылаться на элементы списка товаров. Вот мы и получили
две конструкции, ссылающиеся друг на друга. Больше того, реляционные
базы данных просто-таки сами подсказывают подобные решения.

Давайте разберёмся, как же решить проблему утилизации ненужных данных.

Наименее громоздким мне представляется следующий объект для исследований:

01: #!/usr/bin/perl -w
02:
03: # Осторожно ! Утечка памяти !
04:
05: sub create_ring {
06: my ($length)=@_;
07: my @ring=({'value' => 0})x$length;
08: for (my $i=0; $i<$length; $i++) {
09: $ring[$i]{'next_ref'}=$ring[($i+1)%$length];
10: }
11: return @ring;
12: }
13:
14:


Процедура create_ring создаёт и возвращает массив хэшей, каждый из
которых имеет два ключа. Под именем value хранится некая величина, под
именем "next_ref" ссылка на следующий элемент массива. Последний
элемент несёт ссылку на первый элемент (номер ноль), зацикливая
конструкцию.

Вызывая её в бесконечном цикле и следя за процессами в системе, легко
убедиться, что в приведённой программе есть утечка памяти. Элементы
списка, создаваемого create_ring, не удаляются по той же причине, что и
$a и $b из первого примера: когда выполнение блока доходит до конца,
остаются неудалённые ссылки на элементы списка (как вы помните, эти
ссылки содержатся в хэшах, составляющих список, под именами "next_ref").

Ситуация не изменится даже в том случае, если чуть изменить наш код
(привожу только важные и изменён ные строки):

...
03: # Осторожно ! Утечка памяти !
...
11: return $ring[0];
...
14: while (1) { my $ring=create_ring(10) }


Не поможет и такое изменение:

...
03: # Осторожно ! Утечка памяти !
...
11: return @ring;
...
14: while (1) { my $ring=create_ring(10) }


В двух последних примерах по окончании выполнения блока мы будем терять
ссылку на элемент массива (в первом) или ссылку на сам массив (во
втором), но данные массива, уже никому не доступные, будут бережно
сохраняться системой сборки мусора.

Один из способов освободить память разорвать этот круг (вернее, ring)
вручную, добавив в конец блока соответствующую инструкцию:

...
14: while (1) {
15: my $ring=create_ring(10);
16: $ring->[0]{'next_ref'}=undef;
17: }


Одного разрыва вполне достаточно, чтобы система сборки мусора один за
другим удалила все элементы структуры, но этот способ не выглядит
изящно. Мы просто взяли на себя труд убрать мусор, но так и не заставили
работать над этим интерпретатор Perl. Неужели всё так безнадёжно? Нет! В
Perl есть механизмы, позволяющие растолковать ему все детали нашего
замысла, я говорю об объектно-ориентированном подходе.

Первый пример будет основан на связывании переменной с классом [3].

Я буду рассчитывать на то, что читатель знает об устройстве модулей Perl
и знаком с механизмом связывания объектов с переменными. Вкратце скажу,
что Perl позволяет [4] ассоциировать переменную с классом с помощью
оператора tie. После такого связывания переменная начинает жить по
собственным законам, при любой манипуляции с ней вызываются соответствующие
методы объекта, с которым она связана.

Вот код, реализующий обозначенный подход:

01: #!/usr/bin/perl -w
02:
03: package MyRing;
04:
05: sub create_ring {
06: my ($length)=@_;
07: my @ring=({'value' => 0})x$length;
08: for (my $i=0; $i<$length; $i++) {
09: $ring[$i]{'next_ref'}=$ring[($i+1)%$length];
10: }
11: return @ring;
12: }
13:
14: sub TIESCALAR {
15: my ($class, $length)=@_;
16: return bless create_ring($length), $class;
17: }
18:
19: sub FETCH { return $_[0]; }
20:
21: sub STORE { die "писать в MyRing нельзя.n" }
22:
23: sub DESTROY {
24: my ($self)=shift;
25: $self->[0]{'next_ref'}=undef;
26: }
27:
28: package main;
29:
30: # демонстрация работоспособности
31: {
32: my $a;
33: {
34: tie my $ring, 'MyRing', 10;
35: $ring->[1]{'value'}='test';
36: $a=$ring;
37: }
38: # $ring больше не видна, но данные целы
39: print $a->[0]{'next_ref'}{'value'}."n";
40: # здесь вызывается DESTROY
41: }
42: # $ring=7; # это вызовает метод STORE
43:
44: while (1) { tie my $ring, 'MyRing', 10; }


Для упрощения я не стал выделять модуль, описывающий класс, в отдельный
файл. Связывать объект будем со скалярной переменной, это тоже,
наверное, не лучшее решение: естественнее было бы выбрать массив, а если
бы мы захотели сделать нашу разработку более масштабируемой и
развиваемой, то логичнее было бы выбрать хэш. Но для связывания таких
сложных конструкций нам пришлось бы реализовать множество методов, для
скаляра же достаточно четырёх, эта компактность и наглядность лучше
всего подойдёт для обсуждения в статье.

Итак, в строках 3-27 описан модуль MyRing. Он со держит уже знакомую нам
функцию создания и инициализации массива хэшей (create_ring) и методы,
необходимые для обеспечения работоспособности связанной переменной.

Метод TIESCALAR (строка 14) вызывается в момент связывания, он получает
от оператора tie список дополнительных параметров, в котором у нас будет
только одна величина длина требуемого массива. Получив параметры, метод
создаёт наш массив (вызов create_ring($length)), ассоциирует его с
классом (вызов bless) и возвращает новоиспечённый объект.

Метод FETCH (строка 19) вызывается, когда выполняется чтение значения
связанной переменной. Ему передаётся один параметр сам объект. Наша
реализация FETCH не делает ничего, просто возвращает то, что получила
без изменений.

Метод STORE (строка 21) отвечает за запись в переменную. Я не придумал,
что в него написать, он просто выдаёт грозное сообщение и убивает
программу.

И наконец, метод DESTROY (строка 23) это то, что нам нужно. Он
вызывается автоматически всегда, когда переменная выходит за область
видимости или становится недоступна по другим причинам (например, когда
программа завершается). Наш метод DESTROY разрывает кольцевую структуру
(строка 25 выглядит знакомо, не правда ли?), позволяя системе сборки
мусора довести свою работу до конца.

Со строки 28 начинается основная программа, где мы сейчас и
воспользуемся нашим классом MyRing.

В строках 30-42 приведён небольшой фрагмент кода, демонстрирующий
работоспособность нашей кухни.

В строке 32 мы создаём локальную переменную $a, которая будет
существовать только в пределах блока, находящегося в строках 31-41. Во
вложенном блоке (строки 33-37) мы создаём локальную переменную $ring,
которую сразу же связываем с классом MyRing (строка 35).

Для проверки корректности структуры $ring записываем строку test в её
первый узел (строка 36). Для проверки корректности сборки мусора
сохраняем копию $ring в $a. Что произойдёт, когда вложенный блок
закончится?

Оказывается, метод DESTROY не будет вызван! Это и понятно, мы сохранили
указатель на нашу структуру (помните? $ring является указателем на
массив) в переменной $a, а она всё ещё существует. Переменной $ring не
стало, но пропало только имя, данные целы. Мы убеждаемся в этом в строке
39. Здесь же мы убеждаемся в корректности структуры $ring (теперь $a),
получив доступ к первому узлу как к узлу, следующему после нулевого. А
метод DESTROY будет вызван только тогда, когда исчезнет переменная $a.
Таким образом, все работает правильно.

Обратите внимание и на то, что при выполнении манипуляций с $ring
(строка 35) вызывается метод FETCH, отвечающий за чтение переменной, а
не STORE, ответственный за запись. Действительно, чтобы проделать
операцию, описанную в строке 35, нам пришлось считать значение указателя
$ring, а не записать его. А вот строка 42 не случайно закомментирована.
Операция присвоения автоматически вызывала бы метод STORE, а он бы
аварийно остановил выполнение программы.

Наконец в строке 44 реализован всё тот же бесконечный цикл. На каждом
проходе в его теле вновь создаётся локальная переменная $ring, но теперь
мы связываем её с классом MyRing. Благодаря этому по завершении блока
Perl передаёт управление методу DESTROY, который корректно освобождает
память.

Таким образом, нам больше не приходится удалять кольцевую структуру
руками. Мы научили систему сборки мусора обращаться с нашими данными, и
Perl теперь сам (...почти) защищает нас от утечек памяти.


Я продемонстрировал предельно мудрёную схему, можно ли обойтись меньшей
кровью? Конечно! В Perl грань объектно-ориентированного программирования
весьма размыта. Взгляните на следующий код, в нём нет почти ничего,
напоминающего о его объектно-ориентированности:

01: #!/usr/bin/perl -w
02:
03: package MyRing;
04:
05: sub main::create_ring {
06: my ($length)=@_;
07: my @ring=({'value' => 0})x$length;
08: for (my $i=0; $i<$length; $i++) {
09: $ring[$i]{'next_ref'}=$ring[($i+1)%$length];
10: }
11: return bless @ring, __PACKAGE__;
12: }
13:
14: sub DESTROY {
15: my ($self)=@_;
16: $self->[0]{'next_ref'}=undef;
17: }
18:
19: package main;
20:
21: while (1) { my $ring=create_ring(10); }


Обратите внимание, вызов create_ring в основной программе вообще ни чем
не выдаёт объектно-ориентированную природу переменной $ring. Правда,
саму create_ring нам пришлось чуть доработать, сделав её каким-никаким,
а всё-таки конструктором (вызов bless в строке 11), и экспортировав5 её
в модуль main (имя main::create_ring в строке 5).

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

Я бы не стал говорить, что какой-то из двух приведённых методов решения
проблемы лучше, а другой хуже. У каждого есть свои преимущества и
недостатки. Первый длинноват, но код последователен и легко читается.
Второй компактен, но менее универсален и его логику понять сложнее
(вернее, сложнее разглядеть в нём объектно-ориентированный подход).
Первый код работает чуть медленнее, поскольку переменная связана. Второй
работает чуть быстрее, но допускает выполнение бессмысленных действий,
например:

$ring=7;


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

Гораздо интереснее задать другой вопрос: насколько надёжна наша защита
от утечек памяти? Не будем наивны, она тоже небезупречна и болеет теми
же бо лезнями, что и однострочная программа из самого первого листинга.

Не будем далеко ходить за примером и чуть модифицируем тело цикла из
последнего листинга:

...
21: # Оссторожно ! Утечка памяти !
22: while (1) {
23: my $ring=create_ring(10);
24: my $a;
25: $ring->[0]{'value'}=$a;
26: $a=$ring;
27: }


Утечка произошла по той же самой причине, что и обычно. Когда выполнение
блока подходит к концу и на ступает время собрать мусор, система сборки
обнару живает, что на кольцевую структуру ссылаются две переменных:
$ring и $a. Вторую уничтожить не получается, потому что на неё имеется
ссылка. Где эта ссылка находится, система сборки мусора уже не
разбирается, но мы-то знаем, что она содержится в недрах структуры
$ring. Круг замкнулся, система сборки мусора снова не заметила наш
мусор.

Теперь, я надеюсь, читатель видит и причины возникновения проблемы, и
пути её решения. Одним словом, при создании сложных структур данных
всегда надо помнить, что Perl не сможет угадать ваши мысли, и чётко
представлять, каким образом он интерпретирует ваши команды.

Уточнения:

1
Во многих языках, например в Cи, ситуация обрат-
на: данные могут быть удалены автоматически, даже
если на них имеются указатели. Perl же не требует
повышенной аккуратности при работе с указателя-
ми, всегда поддерживая их работоспособными.

2
Конечно, это жаргон, здесь и далее фразы типа
массив структур следует понимать как массив
указателей на структуры.

3
В Perl понятия класс, модуль и пакет так же
не разделимы, как и понятия объект и перемен-
ная, метод и функция. Поэтому я не буду при-
держиваться строгой объектно-ориентированной
терминологии, в Perl её просто нет.

4
Начиная с версии 5.0.

5
Конечно, это только имитация настоящего экспор-
тирования. Подобный подход не позволит нормаль-
но использовать модуль MyRing в других модулях,
но я не хотел бы здесь уделять чрезмерное внима-
ние созданию модулей в Perl, и мой код демонстри-
рует не хорошие манеры при написании модулей, а
предельно компактное решение.
617 Прочтений •  [Утечки памяти в программах на Perl (perl hash array memory debug)] [08.05.2012] [Комментариев: 0]
Добавил: Ukraine Vova
Ссылки
HTML: 
[BB Url]: 
Похожие статьи
Название Добавил Добавлено
• Утечки памяти в программах на Perl ... Ukraine Vova 08.05.2012
Ни одного комментария? Будешь первым :).
Пожалуйста, авторизуйтесь для добавления комментария.

Проект входит в сеть сайтов «8Gamers Network»

Все права сохранены. 8Gamers.NET © 2011 - 2025

Статьи
Рецензия на Pressure
Рецензия на Pressure
Чтобы обратить на себя внимание, начинающие маленькие разработчики, как правило, уходят в жанры, ...
Рецензия на Lost Chronicles of Zerzura
Рецензия на Lost Chron...
Игры, сделанные без любви и старания, похожи на воздушный шар – оболочка есть, а внутри пусто. Lo...
Рецензия на The Bridge
Рецензия на The Bridge
«Верх» и «низ» в The Bridge — понятия относительные. Прогуливаясь под аркой, можно запросто перей...
Рецензия на SimCity
Рецензия на SimCity
Когда месяц назад состоялся релиз SimCity, по Сети прокатилось цунами народного гнева – глупые ош...
Рецензия на Strategy & Tactics: World War 2
Рецензия на Strategy &...
Название Strategy & Tactics: World War II вряд ли кому-то знакомо. Зато одного взгляда на ее скри...
Рецензия на игру Scribblenauts Unlimited
Рецензия на игру Scrib...
По сложившейся традиции в информационной карточке игры мы приводим в пример несколько похожих игр...
Рецензия на игру Walking Dead: Survival Instinct, The
Рецензия на игру Walki...
Зомби и продукция-по-лицензии — которые и сами по себе не лучшие представители игровой биосферы —...
Обратная связь | RSS | Донейт | Статистика | Команда | Техническая поддержка