Жизненный цикл объектов

В С# общий принцип управления памятью формулируется очень просто: для создания объекта в области «управляемой кучи» (managed heap) используется ключевое слово new. Среда выполнения.NET автоматически удалит объект тогда, когда он больше не будет нужен. Правило в целом вполне понятно, однако возникает один дополнительный вопрос: а как среда выполнения определяет, что объект больше не нужен? Короткий (то есть неполный) ответ гласит, что среда выполнения удаляет объект из памяти, когда в текущей области видимости больше не остается активных ссылок на этот объект. Например:

// Создаем локальный объект класса Саг

public static int Main(string[ ] args)

{

// Помещаем объект класса Саг в управляемую кучу

Саг сЗ = new Car(“Viper", 200, 100);

....

return 0;

} // Если сЗ - единственная ссылка на этот объект, то начиная с этого момента

//он может быть удален

Предположим, что в вашем приложении создано (размещено в оперативной памяти) три объекта класса Саг. Если в управляемой куче достаточно места, мы получим три активные ссылки — по одной на каждый объект в оперативной памяти. Каждая такая активная ссылка на объект в памяти называется также корнем (root). To, ч го у нас получилось, схематически представлено на рис. 3.21.

Рис. 3.21. Ссылки указывают на местонахождение объектов в управляемой куче

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

// Создаем объекты Саг таким образом, чтобы отреагировать на возможную нехватку места //в управляемой куче

public static int Main(string[ ] args)

{

....

Car yetAnotherCar;

try

{

yetAnotherCar = new Car();

}

catcht(OutOfMemoryException e)

{

Console.WriteLine(e.Message);

Console.WriteLine(“Managed heap is FULL1 Running GC..");

}

return 0;

}

Вне зависимости от того, насколько осторожно вы будете создавать объекты, как только место в управляемой куче заканчивается, автоматически запускается сборщик мусора (garbage collector, GC). Сборщик мусора оценивает все объекты, размещенные в настоящий момент в управляемой куче, с точки зрения того, есть ли в области видимости приложения активные ссылки на них. Если активных ссылок на какой-либо объект больше нет или объект установлен в null, этот объект помечается для удаления, и в скором времени память, занимаемая подобными обьектами, высвобождается.

Завершение ссылки на объект

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

Если вам нужно обеспечить возможность удаления объектов из оперативной памяти в соответствии с определенными вами правилами, первое, о чем вам необходимо позаботиться — о реализации в вашем классе метода System.Object.Finаlize(). Заметим между прочим, что реализация этого метода по умолчанию (в базовом классе) ничего не дает. Однако, как это ни странно, в С# запрещено напрямую замещать метод System.Object.Finаlize(). Более того, вы даже не сможете вызвать в вашем приложении этот метод напрямую! Если вы хотите, чтобы ваш пользовательский класс поддерживал метод Finalize(), вы должны использовать в определении этого класса метод, очень похожий на деструктор C++:

// Что-то очень знакомое

public class Car: Object

{

// Деструктор С#?

~Саг()

{

// Закрывайте все открытые объектом ресурсы!

// Далее в С# будет автоматически вызван метод Base FinalizeO

}

}

Если у вас есть опыт работы с C++, то подобный синтаксис вам покажется знакомым. В C++ деструктор класса — это специальный метод класса, имя которого выглядит как имя класса, перед которым стоит символ тильды (~). В C++ гарантируется, что этот метод будет вызван, когда ссылка на объект выходит за пределы области видимости (для типов, размещенных в стеке) или к объекту применяется оператор delete (для объектов, размещенных в области динамической памяти).

При размещении объекта С# в управляемой куче при помощи оператора new среда выполнения автоматически определяет, поддерживает ли ваш объект метод Finalize() (представленный в С# с помощью «деструктороподобного» синтаксиса). Если этот метод поддерживается объектом, ссылка на этот объект помечается как «завершаемая» (finalizable). При этом в специальной внутренней очереди «завершения» (finalization queue) помещается указатель на данный объект. Когда сборщик мусора приходит к выводу, что наступило время удалять данный объект из оперативной памяти, он обращается к этому указателю и запускает деструктор С#, определенный для этого класса, прежде чем будет произведено физическое удаление объекта из памяти.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: