воскресенье, 9 сентября 2012 г.

Наверняка вы думаете о сборке мусора неправильно

Это перевод Everybody thinks about garbage collection the wrong way. Автор: Реймонд Чен.

Добро пожаловать в Неделю CLR 2010. В этом году Неделя CLR будет более философской, чем обычно.

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

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

Сборка мусора - это эмуляция компьютера с бесконечной памятью. Всё остальное - это просто механизмы. "Сбор памяти, которую программа уже не видит" - это один из механизмов.

Теперь, имея в виду такое определение сборки мусора, становится очевидным такое следствие:
Если в run-time программе доступно больше оперативной памяти, чем ей требуется для работы, то пустой менеджер памяти (пустой сборщик мусора, который просто не удаляет память) является допустимым менеджром памяти.
Почему это так? Менеджер памяти будет выделять память программе как только она потребуется. И, по условиям, это выделение всегда будет успешным. Т.е. компьютер с оперативной памятью большей требований программы уже имеет бесконечно много памяти с точки зрения этой программы, а значит эмуляция не нужна.

Конечно, это может быть очевидно, но всё же это также и полезно: потому что пустой менеджер памяти одновременно и просто анализировать и он сильно отличается от менеджеров памяти в реальных программах. Поэтому вы можете использовать его для получения такого результата:
Корректно написанная программа не должна предполагать, что финализаторы (finalizers) отработают в какой-то момент до завершения программы.
Доказательство этого утверждения не сложно: запустите программу на машине с бОльшим количеством оперативной памяти, чем ей потребуется для работы. При этих условиях пустой менеджер памяти является допустимым, а пустой менеджер памяти никогда не запускает финализаторы, потому что он не собирает память.

Сборка мусора эмулирует бесконечную память, но даже если у вас бесконечно много памяти, то всё равно есть некоторые вещи, которые оказывают значительное влияние на другие программы (и даже, возможно, на вашу программу). Например, если вы откроете эксклюзивно файл, то этот файл не будет доступен для других программ (включая другой код в вашей же программе), пока вы его не закроете. Подключение к серверу SQL будет потреблять доступные ресурсы сервера, пока вы его не закроете. Если у вас открыто слишком много подкючений, то при создании очередного подключения вы можете столкнуться с ограничением на число подключений на сервере. Итак, если вы явно не будете закрывать все эти ресурсы, то ваша программа, работающая с "бесконечной" памятью, будет аккумулировать все эти ресурсы, которые никогда не будут освобождаться.

Что это означает для вас: нельзя размещать освобождение ресурсов в финализаторах. Финализаторы - это ремни безопасности, а не способ очистки ресурсов. Когда вы закончите работу с ресурсом, вам нужно явно отпустить его вызовом Close, Disconnect или какой там ещё у него может быть метод очистки.

Более того, оказывается, что корректная программа не должна расчитывать не только на запуск финализаторов до её завершения, но она также не должна расчитывать, что финализаторы отработают в момент завершения: хотя фреймворк .NET попытается запустить их друг за другом, но "плохой" финализатор приведёт к остановке этого процесса. Это может произойти, даже если в этом нет вашей вины. К примеру, у вас может быть описатель сетевого ресурса, финализатор попробует освободить его, но сетевые задержки приведут к тому, что финализатор будет работать более двух секунд, так что .NET просто уничтожит процесс, чем будет ждать завершения работы. Поэтому, результат выше может быть усилен (но только для фреймворка .NET):
Корректно написанная программа не должна предполагать, что финализаторы вообще будут запущены.
Вооружённые этим знанием, теперь вы можете решить проблему этого клиента (терминология оригинала сохранена):
У меня есть класс, использующий Xml­Document. Когда класс покидает область видимости, я хочу удалить файл, но получаю исключение System.IO.Exception: "Процесс не может получить доступ к файлу 'C:\path\to\file.xml', потому что он занят другим процессом". Но если закрыть программу, то блокировка пропадает. Как мне избежать блокировки файла?
Следующая реплика даёт дальнейшую подсказку:
Коллега посоветовал установить переменную Xml­Document в NULL после работы с ним, но разве выход класса из области видимости не сделает ровно это же?

Комментариев нет:

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.

Примечание. Отправлять комментарии могут только участники этого блога.