Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Руководство_по_C#.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
10.01 Mб
Скачать

Установка объектных ссылок в null

Если ранее приходилось создавать СОМ-объекты в Visual Basic 6.0, то должно быть известно, что по завершении их использования предпочтительнее устанавливать эти ссылки в Nothing. На внутреннем уровне счетчик ссылок на объект СОМ уменьшалось на единицу, и когда он становился равным нулю, объект можно было удалять из памяти. Аналогичным образом программисты на C/C++ часто предпочитают устанавливать для переменных указателей значение null, гарантируя, что они больше не будут ссылаться на неуправляемую память.

Из-за упомянутых фактов, вполне естественно, может возникнуть вопрос о том, что же происходит в C# после установки объектных ссылок в null. Для примера изменим метод MyUser() следующим образом:

public static void MyUser()

{

UserInfo ui = new UserInfo();

ui = null;

}

Когда объектные ссылки устанавливаются в null, компилятор C# генерирует CIL-код, который заботится о том, чтобы ссылка больше не ссылалась ни на какой объект. Если теперь снова воспользоваться утилитой ildasm.ехе и заглянуть с ее помощью в CIL-код измененного метода MyUser(), можно обнаружить в нем код операции ldnull (который заталкивает значение null в виртуальный стек выполнения) со следующим за ним кодом операции stloc.O (который присваивает переменной ссылку null):

Однако обязательно следует понять, что установка ссылки в null никоим образом не вынуждает сборщик мусора немедленно приступить к делу и удалить объект из кучи, а просто позволяет явно разорвать связь между ссылкой и объектом, на который она ранее указывала. Благодаря этому, присваивание ссылкам значения null в C# имеет гораздо меньше последствий, чем в других языках на базе С (или VB 6.0), и совершенно точно не будет причинять никакого вреда.

13.2Роль корневых элементов приложения

Давайте рассмотрим вопрос о том, каким образом сборщик мусора определяет момент, когда объект уже более не нужен. Чтобы разобраться в стоящих за этим деталях, необходимо знать, что собой представляют корневые элементы приложения (application roots). Попросту говоря, корневым элементом (root) называется ячейка в памяти, в которой содержится ссылка на размещающийся в куче объект. Строго говоря, корневыми могут называться элементы любой из перечисленных ниже категорий:

  • Ссылки на глобальные объекты (хотя в C# они не разрешены, CIL-код позволяет размещать глобальные объекты).

  • Ссылки на любые статические объекты или статические поля.

  • Ссылки на локальные объекты в пределах кодовой базы приложения.

  • Ссылки на передаваемые методу параметры объектов.

  • Ссылки на объекты, ожидающие финализации

  • Любые регистры центрального процессора, которые ссылаются на объект.

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

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

Чтобы увидеть все это на примере, предположим, что в управляемой куче содержится набор объектов с именами А, В, С, D, Е, F и G. Во время сборки мусора эти объекты (а также любые внутренние объектные ссылки, которые они могут содержать) будут исследованы на предмет наличия у них активных корневых элементов. После построения графа все недостижимые объекты (которыми в примере пусть будут объекты С и F) помечаются как являющиеся мусором. На рисунке показано, как примерно выглядит граф объектов в только что описанном сценарии (линии со стрелками следует воспринимать как "зависит от" или "требует"; например, "Е зависит от G и В", "А не зависит ни от чего" и т.д.).

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

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