Мусоровоз. Категории (генерации) ссылок, страница 2

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

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

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

Управление мусорщиком

В большинстве случаев, мы можем положиться на автоматическую работу сборщика мусора, который будет следить за памятью без нашего вмешательства. Не смотря на это, .NET Framework предоставляет нам методы управления мусоровозом. Эти методы статичны и располагаются у класса GC (Garbage Collector) из пространства имен System. У этого класса есть следующие интересные методы:

·  Collect - вынуждает сборщика мусора внепланово выполнить сборку мусора;

·  CollectionCount - сколько раз объект выживал при сборке мусора;

·  GetGeneration - возвращает информацию о категории, к которой относится объект.

·  GetTotalMemory - объем динамической памяти, выделенной в настоящий момент сборке.

·  WaitForPendingFinalizers - подождать, пока сборка мусора не завершит работу.

Следующий код активизирует сборку мусора и запускает ожидание завершения этого процесса:

GC.Collect();

GC.WaitForPendingFinalizers();

Метод Collect может принимать в качестве значения максимальную категорию (генерацию), которую нужно проверить. Например, следующий вызов заставит систему проверить ссылки, которые попадут в 0-ю и 1-ю категории:

GC.Collect(1);

IDisposable

Неуправляемые ресурсы, на которые не может воздействовать .NET и управлять их распределением/освобождением, должны освобождаться вами явно, и желательно сразу после использования, когда вам ресурсы становятся уже ненужными. Так как .NET не может управлять ресурсами, то не может и освободить их за нас. Мы уже знаем, что освобождение можно писать в деструкторах, но это не единственный способ. Есть еще одна возможность - реализовать интерфейс IDisposable.

Если вы не знаете, что такое интерфейс, то я рекомендую обратиться к книге Библия C#, на диске к которой вы и должны были найти этот файл с документацией. Отложите пока этот документ и вернитесь к нему, когда познакомитесь с интерфейсами.

Интерфейс IDisposable определяет всего один метод, который вы и должны будете реализовать:

public interface IDisposable

{

void Dispose();

}

Этот метод ничего не получает и ничего не возвращает. Его задача просто наделить класс методом, который будет вызываться при уничтожении объекта. Главное преимущество метода Dispose() - вы можете явно вызывать его в коде программы перед уничтожением объекта.

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

public class MyClass: IDisposable

{

// Здесь могут находится методы,

// вашего класса

public Dispose()

{

// Освобождение ресурсов

}

}

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

Давайте вспомним, что генерирует система для каждой визуальной формы ИмяФайла.Designer.cs. Откройте такой файл и найдите в нем следующим метод:

protected override void Dispose(bool disposing)

{

if (disposing && (components != null))

{

components.Dispose();

}

base.Dispose(disposing);

}

Формы в .NET реализуют интерфейс IDisposable и поэтому у них есть реализация метода Dispose. Внутри метода вызывается Dispose переменной components и базового класса.

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

Чтобы проверить, реализует ли объект интерфейс IDisposable, необходимо использовать оператор is, как показано в следующем примере:

MyClass obj = new MyClass();

if (obj is IDisposable)

obj.Dispose();

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

Чтобы Dispose вызывался всегда, его лучше всего поместить в раздел finally блока try:

MyClass obj = new MyClass();

try

{

// Здесь используем объект

}

finally

{

obj.Dispose();

}

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

Тут нужно еще заметить, что если вы используете класс внутри using без переменной, то Dispose вызывать не нужно. Вот тут его вызовет автоматически, и не среда выполнения .NET, а компилятор добавит соответствующий код. Например:

using (MyClass obj = new MyClass())

{

// использование переменной obj класса MyClass

}

Компилятор автоматически развернет такой код в следующую конструкцию:

MyClass obj = new MyClass();

try

{

// Здесь используем объект

}

finally

{

obj.Dispose();

}

То есть вся работа с объектной переменной будет обернута в блок исключения и будет автоматически добавлен вызов метода Dispose.