Российский Государственный Технологический Университет им. К.Э. Циолковского
Опубликован: 02.03.2007 | Доступ: свободный | Студентов: 5677 / 802 | Оценка: 3.96 / 3.45 | Длительность: 27:04:00
ISBN: 978-5-9556-0086-4
Лекция 4:

Объекты

< Лекция 3 || Лекция 4: 123 || Лекция 5 >

Класс GC

Однако, несмотря на вышеуказанные особенности управляемой памяти, в .NET в известных пределах можно управлять сборкой мусора.

В составе библиотеки классов присутствует класс GC, в котором реализованы методы управления сборщиком мусора.

Public-свойства
MaxGeneration Максимальное количество поддерживаемых в управляющей памяти поколений. Статическое
Public-методы
AddMemoryPressure Уведомление среды выполнения о резервировании большого объема неуправляемой памяти, который необходимо учесть при планировании работы сборщика мусора. Статический
Collect Перегруженный. Активизирует процесс сборки мусора. Сборка происходит в отдельном потоке. Поэтому время начала деятельности сборщика остается неопределенным. Статический
CollectionCount Определяет общее количество проходов сборщика для данного поколения объектов. Статический
Equals Определение эквивалентности объектов.
GetGeneration Перегруженный. Возвращает значение номера поколения, содержащего данный объект. Статический
GetTotalMemory Возвращает количество байт, занятых под объекты в управляющей памяти. В зависимости от значения параметра типа bool учитываются (или не учитываются) результаты деятельности сборщика мусора в момент выполнения метода. Сборщик мусора работает в теневом потоке, и в принципе можно немножко подождать результатов его работы. Статический
KeepAlive Ссылается на указанный объект, делая его недоступным для сборщика мусора с момента начала текущей программы до вызова этого метода. Статический
RemoveMemoryPressure Информирует среду выполнения об освобождении области неуправляемой памяти. Эта информация может быть полезна сборщику мусора для планирования работы. Статический
SuppressFinalize Метод обеспечивает уведомление сборщика мусора о том, что данный объект (представляется ссылкой в параметре метода) не подлежит удалению. Статический
ReRegisterForFinalize Сначала защищаем объект от GC путем вызова метода SuppressFinalize. А теперь снимаем с объекта эту защиту. Статический
WaitForPendingFinalizers Приостанавливает текущий поток до тех пор, пока поток, обрабатывающий очередь финализаторов, не обработает всю очередь. Статический

Далее демонстрируются методы управления сборкой мусора. Используется модифицированный класс MemElem:

using System;

namespace GCExample
{
// Объекты этого класса регистрируют деятельность конструктора
// при создании объекта и деструктора при его уничтожении.

public class MemElem
{
 public static long AllElem = 0;
 long N = 0;
 public MemElem(long key)
 {
 AllElem++;
 N = key;
 Console.WriteLine("{0} was created. {1} in memory!", N, AllElem);
 }

~MemElem()
{
AllElem--;
Console.WriteLine("{0} was destroyed. {1} in memory!", N, AllElem);
            
// Смотрим общее количество байтов, занимаемых объектами в куче.
// Значение параметра (false) свидетельствует о том, что эта информация
// требуется незамедлительно, вне зависимости от того, работает ли в данный
// момент в теневом потоке сборщик мусора или нет.
// А в этом случае значения true мы готовы подождать окончания работы
// сборщика мусора, если он в данный момент разбирает завалы.
// Заменить false на true и осознать разницу!
Console.WriteLine("Total Memory: {0}", GC.GetTotalMemory(false));
}
}
    
class GCTestClass
{
private const long maxGarbage = 50;

static void Main()
{
// Можно посмотреть максимальное количество поколений
// (maximum number of generations),
// которое в данный момент поддерживается
// системой сборки мусора.
Console.WriteLine("The highest generation is {0}", GC.MaxGeneration);

// Создан объект - представитель класса GCTestClass. 
GCTestClass gct = new GCTestClass();              

// Начали забивать память всяким хламом.
gct.MakeSomeGarbage();

// Можно посмотреть, в каком "поколении" расположился объект.
Console.WriteLine("gct is in Generation: {0}", GC.GetGeneration(gct));
                   
// Активизировали сборщик мусора для объектов генерации 0.
// Время начала сборки не определено.
GC.Collect(0);
// Смотрим, в каком поколении располагается данный объект. Переместился.
Console.WriteLine("gct is in Generation: {0}", GC.GetGeneration(gct));

// Perform a collection of all generations up to and including 2.
// Сборка мусора во всех поколениях, включая второе.
// Время начала сборки не определено.
GC.Collect(2);

// А в каком поколении gct сейчас? Ну надо же... опять переместился.
Console.WriteLine("gct is in Generation: {0}", GC.GetGeneration(gct));
// И как дела с памятью? 
Console.WriteLine("Total Memory: {0}", GC.GetTotalMemory(false));
Console.Read();
}


// Метод, который всего лишь забивает память всякой ненужной ерундой.
void MakeSomeGarbage()
{
// В данном случае в качестве мусора используются объекты - 
// представители двух замечательных классов... 
//Version vt;
//Object ob;
MemElem mem;           
for (int i = 0; i < maxGarbage; i++)
{
//vt = new Version();
//ob = new Object();
mem = new MemElem(i);
Console.WriteLine("mem is in Generation: {0}", GC.GetGeneration(mem));
}
}
}
}
Листинг 4.5.

Деструктор и метод Finalize

Проблема освобождения неуправляемых ресурсов в .NET решается за счет применения метода Finalize, который наследуется любым классом от object. По умолчанию метод Object.Finalize

void Finalize()...

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

В .NET метод Finalize занимает особое место, и его использование регламентируется следующими ограничениями:

  • предполагается, что управление этому методу передается непосредственно сборщиком мусора при удалении данного объекта;
  • НЕ РЕКОМЕНДУЕТСЯ вызывать метод Finalize из методов, непосредственно не связанных с уничтожением объекта, тем более из методов других классов. Поэтому метод Finalize должен объявляться со спецификатором protected. При этом единственным корректным вызовом метода считается вызов метода Finalize для базового класса с использованием нотации base (...base.Finalize();...) ;
  • в методе Finalize должны освобождаться только те ресурсы, которыми владеет уничтожаемый объект.

Транслятор C# знает об особом статусе метода Finalize и о проблемах, связанных с его использованием. Соответствующее объявление метода в классе сопровождается предупреждением (warning), которое гласит:

Introducing a 'Finalize' method can interfere with destructor invocation.
Did you intend  to declare a destructor?

То есть не лучше ли заменить этот метод деструктором? Дело в том, что для C# транслятора следующие строки кода являются эквивалентными:

~MyClass() // объявление деструктора.
{
  // Здесь реализуются алгоритмы освобождения ресурсов.
}


protected override void Finalize() // Объявление метода финализации.
{
  try
  {
    // Здесь реализуются алгоритмы освобождения ресурсов.
  }
  finally
  {
    base.Finalize(); // Вызов Finalize для базового класса.
  }
}

и если в классе объявляется деструктор (деструктор всегда предпочтительнее), то присутствие метода

protected override void Finalize()...

в том же классе воспринимается как ошибка.

Таким образом, освобождение ресурсов в программе на C# возлагается на Finalize, точнее, на деструктор, который запускается непосредственно GC.

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

Поэтому в .NET предусмотрены альтернативные (детерминированные) способы освобождения неуправляемых ресурсов, которые будут рассмотрены ниже.

< Лекция 3 || Лекция 4: 123 || Лекция 5 >
kewezok kewezok
kewezok kewezok
Елена Шляхт
Елена Шляхт
Объясните плиз в чем отличие а++ от ++а
Почему результат разный?
int a=0, b=0;
Console.WriteLine(a++); //0
Console.WriteLine(++b); //1
a++;
++b;
Console.WriteLine(a); //2
Console.WriteLine(b); //2