В качестве связанного замечания: механизм сборки мусора в .NET 4.0 и последующих версиях был усовершенствован с целью дальнейшего сокращения времени приостановки заданного потока, которая связана со сборкой мусора. Конечным результатом таких изменений стало то, что процесс очистки неиспользуемых объектов поколения 0 или поколения 1 был оптимизирован и позволяет обеспечить более высокую производительность приложений (что действительно важно для систем реального времени, которые требуют небольших и предсказуемых перерывов на сборку мусора).
Тем не менее, важно понимать, что ввод новой модели сборки мусора совершенно не повлиял на способ построения приложений .NET Core. С практической точки зрения вы можете просто разрешить сборщику мусора выполнять свою работу без непосредственного вмешательства с вашей стороны (и
радоваться тому, что разработчики в Microsoft продолжают улучшать процесс сборки мусора в прозрачной манере).
Тип System.GC
В сборке
mscorlib.dll
предоставляется класс по имени
System.GC
, который позволяет программно взаимодействовать со сборщиком мусора, применяя набор статических членов. Имейте в виду, что необходимость в прямом взаимодействии с классом
System.GC
внутри разрабатываемого кода возникает редко (если вообще возникает). Обычно единственной ситуацией, когда будут использоваться члены
System.GC
, является создание классов, которые внутренне работают с неуправляемыми ресурсами. Например, может строиться класс, в котором присутствуют вызовы API-интерфейса Windows, основанного на С, с применением протокола обращения к платформе .NET Core, или какая-то низкоуровневая и сложная логика взаимодействия с СОМ. В табл. 9.2 приведено краткое описание некоторых наиболее интересных членов класса
System.GC
(полные сведения можно найти в документации по .NET Core).
Чтобы проиллюстрировать использование типа
System.GC
для получения разнообразных деталей, связанных со сборкой мусора, обновите операторы верхнего уровня в проекте
SimpleGC
:
using System;
Console.WriteLine("***** Fun with System.GC *****");
// Вывести оценочное количество байтов, выделенных в куче.
Console.WriteLine("Estimated bytes on heap: {0}",
GC.GetTotalMemory(false));
// Значения MaxGeneration начинаются c 0, поэтому при выводе добавить 1.
Console.WriteLine("This OS has {0} object generations.\n",
(GC.MaxGeneration + 1));
Car refToMyCar = new Car("Zippy", 100);
Console.WriteLine(refToMyCar.ToString);
// Вывести поколение объекта refToMyCar.
Console.WriteLine("Generation of refToMyCar is: {0}",
GC.GetGeneration(refToMyCar));
Console.ReadLine;
Вы должны получить примерно такой вывод:
***** Fun with System.GC *****
Estimated bytes on heap: 75760
This OS has 3 object generations.
Zippy is going 100 MPH
Generation of refToMyCar is: 0
Методы из табл. 9.2 более подробно обсуждаются в следующем разделе.
Принудительный запуск сборщика мусора
Не забывайте о том, что основное предназначение сборщика мусора связано с управлением памятью вместо программистов. Однако в ряде редких обстоятельств сборщик мусора полезно запускать принудительно, используя метод
GC.Collect
. Взаимодействие с процессом сборки мусора требуется в двух ситуациях:
•
приложение входит в блок кода, который не должен быть прерван вероятной сборкой мусора;
• приложение только что закончило размещение исключительно большого количества объектов, и вы хотите насколько возможно скоро освободить крупный объем выделенной памяти.
Если вы посчитаете, что принудительная проверка сборщиком мусора наличия недостижимых объектов может принести пользу, тогда можете явно инициировать процесс сборки мусора:
...
// Принудительно запустить сборку мусора
// и ожидать финализации каждого объекта.
GC.Collect;
GC.WaitForPendingFinalizers;
...
При запуске сборки мусора вручную всегда должен вызываться метод
GC.WaitForPendingFinalizers
. Благодаря такому подходу можно иметь уверенность в том, что все финализируемые объекты (описанные в следующем разделе) получат шанс выполнить любую необходимую очистку перед продолжением работы программы. "За кулисами" метод
GC.WaitForPendingFinalizers
приостановит вызывающий поток на время прохождения сборки мусора. Это очень хорошо, т.к. гарантирует невозможность обращения в коде к методам объекта, который в текущий момент уничтожается.
Методу
GC.Collect
можно также предоставить числовое значение, идентифицирующее самое старое поколение, для которого будет выполняться сборка мусора. Например, чтобы проинструктировать исполняющую среду о необходимости исследования только объектов поколения 0, можно написать такой код:
...
// Исследовать только объекты поколения 0.
GC.Collect(0);
GC.WaitForPendingFinalizers;
...
Кроме того, методу
Collect
можно передать во втором параметре значение перечисления
GCCollectionMode
для точной настройки способа, которым исполняющая среда должна принудительно инициировать сборку мусора. Ниже показаны значения, определенные этим перечислением:
public enum GCCollectionMode
{
Default, // Текущим стандартным значением является Forced.
Forced, // Указывает исполняющей среде начать сборку мусора немедленно
Optimized // Позволяет исполняющей среде выяснить, оптимален
// ли текущий момент для удаления объектов.
}
Как и при любой сборке мусора, в результате вызова
GC.Collect
уцелевшие объекты переводятся в более высокие поколения. Модифицируйте операторы верхнего уровня следующим образом:
Console.WriteLine("***** Fun with System.GC *****");
// Вывести оценочное количество байтов, выделенных в куче.
Console.WriteLine("Estimated bytes on heap: {0}",
GC.GetTotalMemory(false));
// Значения MaxGeneration начинаются c 0.
Console.WriteLine("This OS has {0} object generations.\n",