Возможно вы искали: 'Hip Hop All Star'

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

Статей: 87772
Просмотров: 96425698
Игры
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] 18407
• Обзор The Walking ... 18853
• Обзор DMC: Devil M... 19921
• Обзор на игру Valk... 15921
• Обзор на игру Stars! 17810
• Обзор на Far Cry 3 18000
• Обзор на Resident ... 16063
• Обзор на Chivalry:... 17561
• Обзор на игру Kerb... 18021
• Обзор игры 007: Fr... 16667
Превью о играх
• Превью к игре Comp... 18003
• Превью о игре Mage... 14502
• Превью Incredible ... 14763
• Превью Firefall 13523
• Превью Dead Space 3 16378
• Превью о игре SimC... 14772
• Превью к игре Fuse 15479
• Превью Red Orche... 15589
• Превью Gothic 3 16388
• Превью Black & W... 17402
Главная » Статьи » Разное » Циклические ссылки и сборщик мусора в Perl (perl trash garbage)

Циклические ссылки и сборщик мусора в Perl (perl trash garbage)

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

From: Игорь Гариев <gariev at hotmail dot com>
Date: Mon, 1 Nov 2007 18:21:07 +0000 (UTC)
Subject: Циклические ссылки и сборщик мусора в Perl

Оригинал: http://kiev.pm.org/?q=node/15

Улучшаем Perl код "уничтожением"

Автор статьй: Игорь Гариев(gariev at hotmail dot com)
Оригинал на английском: Better Code Through Destruction
Перевод выполнен tba

Ларри Уолл сказал, что перл позволяет делать простые вещи просто, а
сложные делает возможными. Перл хорош как для написания двухстрочных
скриптов, которые спасают мир в последнюю минуту (по крайней мере
спасают вас и ваш проект), так и для сложных проектов. Однако, хорошая
техника программирования на перл может сильно различаться в маленьких
и больших приложениях. Рассмотрим, например, сборщик мусора (garbage
collector) в перл. Он освобождает программиста от забот, связанных с
управлением памятью... до те пор, пока программист не создаст
циклические ссылки (circular references).

Сборщик мусора в перле считает ссылки. Когда счетчик достигнет нуля
(что означает, что никто не ссылается), перл уничтожает объект. Подход
прост и эффективен. Однако, при циклических ссылках (когда объект A
ссылается на объект B, а объект B ссылается на объект A) возникает
проблема. Даже если ничего в программе не будет ссылаться на A и B,
счетчик ссылок на них никогда не достигнет нуля. Объекты A и B не
уничтожатся. Если в коде они создаются снова и снова (возможно в
цикле), мы получим утечку памяти (memory leak). Количество памяти,
выделяемое программе увеличивается без разумных на то оснований и
никогда не сможет уменьшится. Такой эффект допустим в простом
выполнился-завершился скрипте, но не допустим в программах работающих
24x365, таких как mod_perl или FastCGI окружения, или как
самостоятельные серверы.

Циклические ссылки иногда очень полезны. Типичный пример - древовидная
структура данных. Для навигации в обоих направлениях - от корня к
листьям и наоборот - родительский узел хранит список дочерних, а
дочерний ссылается на родительский. Получаем циклические ссылки. Во
многих CPAN модулях модель данных реализована таким образом, включая
HTML::Tree, XML::DOM и Text::PDF::File. Все эти модули содержат метод
для освобождения памяти. Клиентское приложение должно вызвать этот
метод, когда объект перестает быть нужен. Однако требование явного
вызова не является изящным и может содержать небезопасный код:

###
## Код с утечкой памяти
#
use HTML::TreeBuilder;

foreach my $filename (@ARGV) {
my $tree = HTML::TreeBuilder->new;
$tree->parse_file($filename);
next unless $tree->look_down('_tag', 'img');
##
## Здесь происходит настоящя работа (например, извлечение изображений)
## ...
## и утечка памяти
##
$tree->delete;
}


Проблема в инструкции next: HTML документы без <img ... тэгов не
освобождаются. В действительности, любой вызов next, last, return
(внутри подпрограммы) или die (внутри eval {} блока) явлется
небезопасным и приводит к утечке памяти. Конечно, можно поместить
освобождающий код в блок continue для last или next, или писать код
для уничтожения дерева перед каждым return или die, но код быстро
становится перегруженным.

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

##
## Применяется специальный служебный объект
##
use HTML::TreeBuilder;

foreach my $filename (@ARGV) {
my $tree = HTML::TreeBuilder->new;
$tree->parse_file($filename);

my $sentry = Sentry->new($tree);
next unless $tree->look_down('_tag', 'img');
##
## next, last or return are safe here.
## Tree will be deleted automatically.
##
}

package Sentry;

sub new {
my $class = shift;
my $tree = shift;
return bless {tree => $tree}, $class;
}

sub DESTROY {
my $self = shift;
$self->{tree}->delete;
}


Заметим, что в конце цикла нет необходимости явно вызывать
$tree->delete. Фокус прост. Когда в процессе выполнения программный
поток покидает блок, $setry уничтожается, потому что он не участвует в
циклических ссылках. Код метода DESTROY пакета Sentry, в свою очередь,
вызывает метод delete объекта $tree. Это решение для всех случаев;
память будет возвращена как только вы покидаете блок.

Наконец, нет неоходимости в написании своего класса Sentry. Используем
Object::Destroyer, написанный Адамом Кеннеди(Adam Kennedy). Как можно
догадаться и названия, это объект, который уничтожает другие объекты:

##
## Решение с использованием Object::Destroyer
##
use HTML::TreeBuilder;
use Object::Destroyer 2.0;

foreach my $filename (@ARGV) {
my $tree = HTML::TreeBuilder->new;
my $sentry = Object::Destroyer->new($tree, 'delete');
$tree->parse_file($filename);
next unless $tree->look_down('_tag', 'img');
##
## You can safely return, die, next or last here.
##
}


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

Наконец, можно уничтожить любую струтуру данных, не только объекты,
если обеспечить код для этого. Передадим ссылку на подпрограмму или
анонимную подпрограмму:

##
## Unblessed структура данных с циклическими ссылками,
## которая не может самостоятельно "распутаться"
##

use Object::Destroyer 2.0;

while (1) {
my (%a, %b);
$a{b} = %b;
$b{a} = %a;
my $sentry = Object::Destroyer->new( sub { undef $a{b} } );
}


Ради смеха закомментируйте строку с $sentry объектом и понаблюдайте за
потребляемой скриптом памятью.


Использование Object::Destroyer как враппера.

Object::Destroyer может также облегчить жизнь авторам модулей. Если вы
написали библиотеку с циклическими ссылками, то можно или попросить
пользователей явно вызывать методы удаления, или использовать новую
возможность перл (стабильна начиная с perl 5.8; см. Scalar::Util) -
слабые ссылки (weak references). Слабые ссылки не увеличивают счетчики
ссылок на объекты, поэтому сборщик мусора перл может безопасно
собирать ссылки. В примере с деревом все ссылки от листьев к родителям
(но не наоборот, иначе мы потеряем дерево) могут буть слабыми. Когда
последняя ссылка на корневой узел уничтожится, рекурсивно уничтожатся
и ссылки на все дочерние узлы. Их счетчики станут равными нулю и перл
освободит память, занимаемую всеми ветвями дерева.

Действительно, некоторые CPAN модули используют подобный подход
(XML::Twig). Однако такое решение работает только если слабые ссылки
доступны. Во-вторых это может требовать довольно большого повторения
кода (в коде XML::Twig 3.26 девять вызовов weaken).

В качестве альтернативы можно использовать Object::Destroyer во
внутренней библиотеке. Это может работать как почти прозрачный враппер
над объектом:

##
## Object::Destroyer как враппер
##
package My::Tree;
use Object::Destroyer 2.0;

sub new {
my $class = shift;
my $self = bless {}, $class;
$self->populate;
return Object::Destroyer->new( $self, 'release' );
}

sub release{
## действительное выделение памяти
}

sub say_hello{
my $self = shift;
print "Hello, I'm object of class ", ref($self), "n";
}

package main;
{
my $tree = My::Tree->new;
$tree->say_hello;
##
## $tree->release будет вызываться Object::Destroyer'ом;
##
}


Объект $tree в клиентском коде в действительности является объектом
Object::Destroyer, который пересылает все вызываемые методы внутренним
объектам класса My::Tree. Метод say_hello не видит никакой разницы -
он получает исходный $self объект. Изменения в коде минимальны и легко
отслеживаются.

Этот подход также имеет ограничения: объекты не должны обращаться к
атрибутам объекта напрямую (например, $tree->{age}). В любом случае
подобная практика - плохой стиль. В добавок, вызовы методов объекта
клиентским кодом приводят к небольшим задержкам по времени. Вызовы из
кода библиотеки не затрагиваются.


Исключения и освобождение ресурсов

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

Рассмотрим сигналы как пример. Предположим, что у нас имеется
некоторый потенциально долго выполняемый (или даже бесконечный) код.
Мы не хотим, чтобы скрипт подвис и хотим прервать его выполнение.
Сигналы как раз для таких задач. Однако первая же попытка написания
хорошего кода может быть неуклюжей:

##
## Пример сигнала 1. Интуитивный.
##

eval{
local $SIG{ALRM} = sub { die "Timed outn" };
alarm(5);
long_running_code();
## Отменяем сигнал, если код выполнится за 5 сек.
alarm(0);
};

if ($@ && $@ eq "Timed outn") {
## Обработка ошибок
}


Код будет работать пока в long_runnig_code() не выполнится die. В этом
случае блок eval поймает die, но не сигнал. Если это случится в
программе, которая должа работать 24 часа в сутки, программа
остановится через 5 секунд.

Следующий пример намного лучше; вообще-то это пример рабочего кода.
Этого достаточно для большинства приложений. Однако он не совсем
пуленепробиваем.

##
## Пример сигнала 2. Стандартное решение.
##

eval{
local $SIG{ALRM} = sub { die "Timed outn" };
alarm(5);
long_running_code();
## Отменяем сигнал, если код выполнится за 5 сек.
alarm(0);
};
## Отменяем сигнал, если long_running_code() "умирает".
alarm(0);


Сколько раз сигнал будет отменен в следующем коде?

##
## Пример сигнала 3. Коварный код.
##
LOOP:

foreach my $arg (1..3) {
eval{
local $SIG{ALRM} = sub { die "Timed outn" };
alarm(5);
long_running_code($arg);
alarm(0);
};
alarm(0);
}

sub long_running_code{ last LOOP; }


Упс, ни разу.
RAII решение более надежно:

##
## Пример сигнала 4.
## Ресурсы под контролем Object::Destroyer
##

eval{
local $SIG{ALRM} = sub { die "Timed outn" };
alarm(5);

my $sentry = Object::Destroyer->new( sub {alarm(0)} );
long_running_code();
};


Вне зависимости от того, каким образом завершится блок eval, перл
уничтожит объект $sentry. Уничтожение вызовет alarm(0).

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

##
## Файловая блокировка.
##
use Fcntl ':flock';

open my($fh), ">$filename.lock";
eval{
flock($fh, LOCK_EX);
my $sentry = Object::Destroyer->new( sub {flock($fh, LOCK_UN)}
);
##
## Здесь действительный код.
## die безопасно.
##
};
##
## Семафор
##

use Thread::Semaphore;
use Object::Destroyer;

my $s = Thread::Semaphore->new();

eval{
$s->down;
my $sentry = Object::Destroyer->new( sub { $s->up } );
##
## Здесь критический код, die безопасно
##
};

##
## Блокировка таблицы MySQL
##

use DBI;

my $dbh = DBI->connect("dbi:mysql:...", "", "");

eval{
$dbh->do("LOCK TABLE table1 READ");
my $sentry = Object::Destroyer->new(
sub { $dbh->do("UNLOCK TABLES"); }
);
##
## Опять же основной код здесь
##
};


Код чистый, простой и довольно самодоументируемый.


Простые транзакции

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

use Object::Destroyer 2.0;

my ($account1, $account2) = (15, 15);

printf("Account1=%d, Account2=%d, Total=%dn",
$account1, $account2, $account1+$account2);

eval {
my $coderef = create_savepoint($account1, $account2);
my $sentry = Object::Destroyer->new($coderef);

die "before changes" if rand > 0.7;
$account1 += 3;
die "after account 1 was modified" if rand > 0.7;
$account2 -= 3;
die "after account 2 was modified" if rand > 0.7;
##
## Здесь предполагается подтверждение транзакций
## и $sentry может быть освобождена.
## $coderef->() не может быть вызвана.
##
$sentry->dismiss;
die "after transaction is committed" if rand > 0.7;
};

print "Died $@" if $@;
printf("Account1=%d, Account2=%d, Total=%dn",
$account1, $account2, $account1+$account2);

sub create_savepoint {
## Save references to variables and their current values
my @vars;
foreach my $ref (@_) {
die "Can remember only scalar values" unless ref($ref) eq 'SCALAR';
push @vars, { ref => $ref, value => $$ref };
}

## Замыкание для восстановления их значений
return sub {
foreach my $var (@vars) {
${ $var->{ref} } = $var->{value};
}
};
}


Выполним скрипт несколько раз. Из-за rand он будет ломаться на
некоторой линии, но получить суммарное число отличное от 30
невозможно.


Полезные ссылки

RAII несомненно новая техника. Она очень популярна в мире C++. Если вы
не боитесь C++, вы можете найти интересным стандартный
контейнер auto_ptr и эффетивное использование auto_ptr.
Нестандартный класс ScopeGuard обеспечивает лексически определенное
управление памятью в C++.

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

В CPAN есть также несколько модулей для лексически ограниченного
управления памятью, но Object::Destroyer мой любимый. Вы также
можете взглянуть на Hook::Scope, Scope::Guard и
Sub::ScopeFinalizer.

Наконец, в Object Oriented Exception Handling in Perl обсуждается,
почему исключения бесценны в больших проектах.
490 Прочтений •  [Циклические ссылки и сборщик мусора в Perl (perl trash garbage)] [08.05.2012] [Комментариев: 0]
Добавил: Ukraine Vova
Ссылки
HTML: 
[BB Url]: 
Похожие статьи
Название Добавил Добавлено
• Циклические ссылки и сборщик мусора... 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 | Донейт | Статистика | Команда | Техническая поддержка