4_C#_сборка_мусора_исключения_Disposable

Download Report

Transcript 4_C#_сборка_мусора_исключения_Disposable

Жизненный цикл объекта
 Создание объекта при вызове оператора new
• Под объект выделяется область памяти из управляемой кучи.
• Область памяти инициализируется при работе конструктора.
• Возможно захватывается ресурс, например, открывается файл
или устанавливается соединение с базой данных.
 Использование объекта
• Вызываются методы объекта, производится доступ к полям
данных объекта.
 Уничтожение объекта (точное время не определено)
• При помощи деструктора (финализатора) объект превращается в
область памяти.
• Область памяти возвращается системе.
 Освобождением занятых ресурсов занимается сборщик мусора.
Деструктор
 В классе можно определить деструктор.
 В отличие от C++ порядок вызова деструкторов в C# не определен.
Перед освобождением памяти сборщик мусора вызывает деструктор,
но возможны ситуации, когда деструктор для объекта не вызывается
совсем.
 Определять свои деструкторы нужно только в случае крайней
необходимости - они создают значительную нагрузку на систему.
 На самом деле наличие в классе деструктора означает неявное
переопределение метода Finalize
class T {
~T() { Console.WriteLine(“T destructed”); }
}
class T {
protected override void Finalize(){
try { Console.WriteLine(“T destructed”);}
finally { base.Finalize();}
}
}
Сборщик мусора ( Garbage Collector)
CLR использует модель сборки с поколениями
0 – (128-256-512 K) – динамически настраивается
1 – (2 Mб)
2 – (10 Mб)
 Сборщик мусора начинает работу, когда заполнено поколение 0,
неиспользуемые объекты освобождаются, а все остальные
перемещаются в поколение 1.
 Для больших объектов (> 85000 байт) своя куча и свой алгоритм
работы GC. Массивные объекты сборщик мусора не перемещает.
 Алгоритмы сборки отличаются в debug и release версиях и в
различных версиях OC.
Информация:
1. Рихтер Дж. CLR via C#. Программирование на платформе Microsoft .NET
Framework 2.0 на языке C#. - Изд. Microsoft Press. Русская редакция, 2007.
2. Маони Стивенс (Maoni Stephens). Представляем кучу для массивных объектов MSDN MAGAZINE , июнь 2008.
Сборка мусора для объектов с завершением
 Перед вызовом Ctor объекты с завершением (с Dtor) заносятся в
специальный список объектов, для которых надо вызвать Dtor перед
освобождением памяти.
 При сборке мусора в поколении 0 объекты с завершением не
уничтожаются, а заносятся в специальную очередь завершения.
Очередь завершения обрабатывает специальный высокоприоритетный
поток.
В каком случае Dtor может быть не выполнен?
 После завершения работы приложения CLR вызывает Finalize() для
каждого объекта с завершением, но если время возврата из него более
2 сек, CLR завершает процесс и остальные методы Finalize() не
вызываются.
Исключения
 В коде, использующем библиотеки классов, обработка ошибок во
время выполнения программы должна быть разделена на две части :
• генерация сообщения об ошибке, которая не может быть
обработана локально;
• обработка ошибки в том месте, где достаточно информации для
анализа нестандартной ситуации и ее обработки.
 Механизм исключений C# дает возможность писать устойчивый и
простой в сопровождении код.
 Можно передать произвольный объем информации в точку
обработки ошибки.
Исключения в C# и C++
 Механизм исключений в C# почти полностью совпадает с
механизмом исключений в C++. Отличия:
• все исключения C# должны быть экземплярами классов,
производных от класса System.Exception;
• в C# можно использовать блок finally, который выполняется в
любом случае, независимо от того, брошено исключение или нет;
• в C# для системных исключений (переполнение, деление на нуль
или нулевая ссылка) также определены классы, которые
обрабатываются наравне с исключениями уровня приложения.
 Исключение явно порождается выражением throw
throw new FileNotFoundException(“test.dat”);
Тип System.Exception
 Все исключения должны иметь тип System.Exception или быть его
потомками
public class Exception : ISerializable
{
…
public Exception();
public Exception( string message );
public Exception( string message, Exception innerException );
public virtual string Message { get;}
public Exception InnerException { get;} // предыдущее исключение,
// если данное было сгенерировано при
// обработке предыдущего.
public virtual string StackTrace { get;} // строка с именами
// и сигнатурами последовательных вызовов,
// которые привели к генерации исключения.
}
Иерархия классов-исключений ( часть классов)
System.Object
System.Exception
System.SystemException
System.ArgumentException
System.ArithmeticException
System.ArrayTypeMismatchException
System.BadImageFormatException
System.FormatException
System.IndexOutOfRangeException
System.InvalidCastException
System.NullReferenceException
System.OutOfMemoryException
System.ExecutionEngineException
System.StackOverflowException
...
System.Object
System.Exception
System.SystemException
System.IO.IOException
System.IO.DirectoryNotFoundException
System.IO.EndOfStreamException
System.IO.FileLoadException
System.IO.FileNotFoundException
System.IO.PathTooLongException
Блок finally
 Выполняется в любом случае, независимо от того, брошено
исключение или нет.
 Если нет исключения, выполняются все операторы в блоке try, а
затем операторы блока finally.
 Если брошено исключение, CLR просматривает стек вызовов в
поиске обработчика с нужным типом фильтра, и выполняет
•
сначала блок finally в тех методах, где нет обработчика с
требуемым фильтром
•
затем блок catch
•
затем finally из блока try с обработчиком
 Допустимые конфигурации
•
try – catch – finally
•
try – catch
•
try – finally
Порядок обработки исключений
static void Main ( string[] args)
{… код C# …
Abc abc = new Abc();
try {
abc.Method1();
… код C# … 1
}
сatch (Exception1 ex) {
… 1 обработка исключений класса
… Exception1 и его потомков
}
catch (Exception2 ex) {
… 2 обработка исключений класса
… Exception2 и его потомков
}
finally { … 1 … }
}
partial class Abc {
public void Method1()
{ … код C#
partial class Abc {
public void Method2()
{ … код C#
try {
… код C# … 5
if( < условие>)
throw new ExceptionN();
… код C# … 6
try {
… код C# … 2
Method2();
… код C# … 3
}
catch (Exception3 ex) {
… 3 обработка исключений класса
… Exception3 и его потомков
}
finally { … 2 …}
… код C# … 4
}
}
сatch (Exception1 ex) {
… 4 обработка исключений класса
… Exception1 и его потомков
}
catch (Exception4 ex) {
… 5 обработка исключений класса
… Exception4 и его потомков
}
finally { … 3 … }
… код C# … 7
}
<условие>== false
код 2 код 5 код 6 finally 3 код 7 код 3 finally 2 код 4 код 1 finally 1
<условие>== true throw Exception1
код 2 код 5 catch 4 finally 3 код 7 код 3 finally 2 код 4 код 1 finally 1
<условие>== true throw Exception3
код 2 код 5 finally 3 catch 3 finally 2 код 4 код 1 finally 1
<условие>== true throw Exception2
код 2 код 5 finally 3 finally 2 catch 2 finally 1
Задача освобождения ресурсов. Решение в C++
void main ()
{ try
{ ... код С++
f();
... код С++
}
catch (exception ex)
{ ... код С++
}
}
void f()
{ Resource R ();
// R.Lock();
Lock_Resource LR(R);
fInner(R);
// R.Unlock();
}
void fInner (Resource& rc)
{ ... код бросает исключение
}
class Resource
{ public:
void Lock()
{ ... захват ресурса }
void Unlock()
{ ... освобождение ресурса }
Resource () {...}
private:
... данные
};
class Lock_Resource
{ public:
Lock_Resource (Resource &rci)
{ rc = rci; rc.Lock();}
~Lock_Resource() { rc.Unlock();}
private:
Resource& rc;
};
Задача освобождения ресурсов. Решение в C#.
public static
void Main ( string[] args)
{ try
{ ... код С#
f();
... код С#
}
catch (exception ex)
{ ... код С#
}
}
public void f()
{ Resource R ();
R.Lock();
try { fInner(R);}
finally
{ R.Unlock();}
}
void fInner (Resource& rc)
{ ... код бросает исключение
class Resource
{ public void Lock()
{ ... захват ресурса }
public void Unlock()
{ ... освобождение ресурса }
public Resource () {...}
private:
... данные
}
}
Типы с явным освобождением ресурсов
 В типах, которые поддерживают модель детерминированного
освобождения ресурсов, следует реализовать метод
void Dispose();
в котором размещают код, освобождающий ресурсы.
 Оператор
using - краткая форма try-finally
class A : IDisposable { }
class B : IDisposable { }
using(a = new A())
using(b = new B())
{
// …
}
a = new A();
try {
b = new B();
try {
…
}
finally { b.Dispose(); }
}
finally {
a.Dispose();
}
Пример реализации метода Dispose()
public class MyResource: IDisposable
{
private IntPtr handle; // unmanaged resource
private Component component = new Component(); // managed resource
private bool disposed = false;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing) {
// disposing == true
явный вызов из Dispose()
// disposing == false вызов из деструктора
if ( !this.disposed )
{
if ( disposing ) // явный вызов Dispose()
{ component.Dispose(); // Dispose managed resources.
}
CloseHandle(handle); // Clean up unmanaged resources
}
disposed = true;
}
~MyResource() { Dispose(false); }
}