Оригинал: http://www.micel.cn/article.asp?id=1698 .
Знаете ли вы механизм сборки мусора в .Net? Можете ли вы кратко описать, как работает GC? Как мы можем эффективно управлять памятью? Какова роль объекта, экземпляр которого создается в теле оператора using?
Этот раздел организован следующим образом: 1. Типы сетей и распределение памяти. 2. Как работает сборщик мусора GC. 3. Что такое неуправляемые ресурсы. 4. Как эффективно освобождать ресурсы объектов. Резюме Давайте начнем изучение этого раздела прямо сейчас.
1..Типы сетей и распределение памяти
Все типы в Net являются производными (прямо или косвенно) от типа System.Object.
Типы в CTS делятся на две категории — ссылочные типы (ссылочные типы, также называемые управляемыми типами), которые размещаются в куче памяти, и типы значений. Типы значений размещаются в стеке. Как показано на картинке
Типы значений находятся в стеке: первыми вошли, последними вышли. Переменные типа значения имеют порядок жизни. Это гарантирует, что переменные типа значения высвободят ресурсы до того, как они будут вытеснены из области действия. Проще и эффективнее, чем ссылочные типы. Стек распределяет память от старших адресов к младшим.
Ссылочный тип выделяется в управляемой куче (Managed Heap), а переменная объявляется и сохраняется в стеке. Когда для создания объекта используется new, адрес объекта сохраняется в этой переменной. Напротив, управляемая куча распределяет память от младших адресов к старшим, как показано на рисунке.
2. Как работает сборщик мусора GC
На приведенном выше рисунке, когда срок действия dataSet истекает, мы не отображаем уничтожение объекта, а объекты в куче продолжают существовать, ожидая переработки GC.
Рекомендуется, но не обязательно, чтобы сборщик мусора поддерживал устаревание объектов посредством генерации. Поколение — это единица объектов с относительным возрастом в памяти. Объект
Кодовое имя или возраст идентифицирует поколение, к которому принадлежит объект. В жизненном цикле приложения объекты, созданные совсем недавно, относятся к более новому поколению и имеют более высокие значения, чем объекты, созданные ранее.
Нижний номер субкода. Объектный код в самом последнем поколении равен 0.
При создании нового объекта необходимо сначала выполнить поиск в свободном связанном списке, чтобы найти наиболее подходящий блок памяти, выделить его, настроить связанный список блоков памяти и объединить фрагменты. Новую операцию можно выполнить почти за O(1), добавив 1 к верхнему указателю кучи. Принцип работы таков: когда оставшегося места в управляемой куче недостаточно или пространство Генератора 0 заполнено, запускается сборщик мусора и начинает освобождать память. В начале сборки мусора GC сжимает и корректирует память кучи, а объекты концентрируются вверху. GC будет занимать определенное количество процессорного времени при сканировании мусора. Исходный алгоритм GC действительно сканирует всю кучу, что неэффективно. Текущий сборщик мусора делит объекты в куче на три поколения. Самая последняя запись в куче — поколение 0, за ним следуют поколение 1 и поколение 2. Первый сборщик мусора сканирует только поколение 0. Если освобожденного пространства достаточно для текущего использования, нет необходимости проверять объекты других поколений. Таким образом, GC создает объекты более эффективно, чем C++, и ему не нужно сканировать всю кучу. Улучшения производительности, обеспечиваемого стратегией сканирования и стратегией управления памятью, достаточно, чтобы компенсировать время ЦП, занимаемое ГХ.
3. Что такое неуправляемые ресурсы?
Обычный неуправляемый ресурс — это объект, который обертывает ресурс операционной системы, например файл, окно или сетевое подключение. Для таких ресурсов сборщик мусора может отслеживать время существования объекта, который является оболочкой неуправляемого ресурса, но он знает, как это сделать. очистите эти ресурсы. К счастью, метод Finalize(), предоставляемый .net Framework, позволяет правильно очистить неуправляемые ресурсы до того, как сборщик мусора переработает такие ресурсы. Вот несколько распространенных неуправляемых ресурсов: кисти, объекты потока, объекты компонентов и другие ресурсы (Object, OdbcDataReader, OleDBDataReader, Pen, Regex, Socket, StreamWriter, ApplicationContext, Brush,
Компонент, ComponentDesigner, Контейнер, Контекст, Курсор, FileStream,
Шрифт, значок, изображение, матрица, таймер, подсказка). (См. MSDN)
4. Как эффективно освободить неуправляемые ресурсы.
GC не может управлять неуправляемыми ресурсами, так как же освободить неуправляемые ресурсы? .Net предоставляет два метода:
(1) Деструктор: когда сборщик мусора перерабатывает ресурсы неуправляемого объекта, он вызывает метод финализации объекта Finalize() для очистки ресурсов. Однако из-за ограничений правил работы сборщика мусора он вызывает метод Finalize объекта. Ресурс не будет освобожден один раз, а объект будет удален после второго вызова.
(2) Наследуйте интерфейс IDisposable и реализуйте метод Dispose(). Интерфейс IDisposable определяет шаблон (с поддержкой на уровне языка), предоставляет определенный механизм освобождения неуправляемых ресурсов и позволяет избежать присущих устройству проблем со сборкой мусора. - связанные с этим вопросы.
Чтобы лучше понять механизм сборки мусора, я специально написал часть кода и добавил подробные комментарии. Определите один класс FrankClassWithDispose (наследует интерфейс IDisposable), FrankClassNoFinalize (без финализатора), FrankClassWithDestructor (определяет деструктор).
Конкретный код выглядит следующим образом:
Код
1 с использованием системы;
2 с использованием System.Collections.Generic;
3 с использованием System.Text;
4 с использованием System.Data;
5 с использованием System.Data.Odbc;
6 с использованием System.Drawing;
7 // Закодировано Фрэнком Сюй Леем, 18 февраля 2009 г.
8 // Изучите управление памятью .NET
9 // Сборщик мусора Сборщик мусора. Размещенные ресурсы могут быть возвращены при необходимости в соответствии с политиками.
10 // Но сборщик мусора не знает, как управлять неуправляемыми ресурсами. Например, сетевое подключение, подключение к базе данных, кисть, компонент и т. д.
11 //Два механизма решения проблемы освобождения неуправляемых ресурсов. Деструктор, интерфейс IDispose
12 // счетчик ссылок COM
13 // Ручное управление C++, новое удаление
14 // Автоматическое управление ВБ
15 пространство имен MemoryManagement
16 {
17 // Наследуем интерфейс IDisposable, реализуем метод Dispose и освобождаем ресурсы экземпляра FrankClassDispose.
18 публичный класс FrankClassWithDispose: IDisposable
19 {
20 частный OdbcConnection _odbcConnection = null;
двадцать один
22 // Конструктор
23 публичный FrankClassWithDispose()
двадцать четыре {
25 если (_odbcConnection == null )
26 _odbcConnection = новый OdbcConnection();
27 Console.WriteLine("FrankClassWithDispose создан");
28 }
29 //Метод тестирования
30 публичных недействительных DoSomething()
31 {
32
33 /**/ /// /здесь код, чтобы что-то сделать
34 возврата;
35}
36 // Реализация Dispose и освобождение ресурсов, используемых этим классом
37 публичная недействительность Dispose()
38 {
39, если (_odbcConnection != null )
40 _odbcConnection.Dispose();
41 Console.WriteLine("FrankClassWithDispose удален");
42 }
43}
44 // Finalize не реализован, подождите, пока GC перезапустит ресурсы экземпляра FrankClassFinalize, и перезапустите их непосредственно во время работы GC.
45 публичный класс FrankClassNoFinalize
46 {
47 частный OdbcConnection _odbcConnection = null;
48 //Конструктор
49 публичный FrankClassNoFinalize()
50 {
51 если (_odbcConnection == null )
52 _odbcConnection = новый OdbcConnection();
53 Console.WriteLine("FrankClassNoFinalize создан");
54 }
55 //Метод тестирования
56 публичная недействительность DoSomething()
57 {
58
59 // GC.Collect();
60 /**/ /// /здесь код, чтобы что-то сделать
61 возврат;
62 }
63}
64 // Реализуйте деструктор, скомпилируйте его в метод Finalize и вызовите деструктор объекта.
65 // Когда GC работает, ресурс освобождается не при первом вызове, а только при втором вызове.
66 //Ресурсы экземпляра FrankClassDestructor
67 // CLR использует независимый поток для выполнения метода Finalize объекта. Частые вызовы снижают производительность.
68 публичный класс FrankClassWithDestructor
69 {
70 частное OdbcConnection _odbcConnection = null;
71 //Конструктор
72 публичный FrankClassWithDestructor()
73 {
74 если (_odbcConnection == null )
75 _odbcConnection = новый OdbcConnection();
76 Console.WriteLine("FrankClassWithDestructor создан");
77 }
78 //Метод тестирования
79 публичная недействительность DoSomething()
80 {
81 /**/ /// /здесь код, чтобы что-то сделать
82
83 возврат;
84}
85 // Деструктор, освобождает неуправляемые ресурсы
86 ~ ФранкКлассСДеструктор()
87 {
88 если (_odbcConnection != null )
89 _odbcConnection.Dispose();
90 Console.WriteLine( "FrankClassWithDestructor удален");
91 }
92 }
93}
94
Используется экземпляр неуправляемого объекта OdbcConnection. Построенный клиент был кратко протестирован. Клиентский код выглядит следующим образом:
Код
1 с использованием системы;
2 с использованием System.Collections.Generic;
3 с использованием System.Text;
4 с использованием System.Data;
5 с использованием MemoryManagement;
6 // Закодировано Фрэнком Сюй Леем, 18 февраля 2009 г.
7 // Изучите управление памятью .NET.
8 // Тестирование возвращенных неуправляемых объектов.
9 // Тесты на неуправляемый код, сравнение
10 // Для управляемого кода сборщик мусора может переработать его самостоятельно или реализовать IDisposable, вызвать метод Dispose() и активно его освободить.
11 пространство имен MemoryManagementClient
12 {
13 программ занятий
14 {
15 static void Main( string [] args)
16 {
17
18 /**/ /////////////////////////////////////////// //(1 ) / //////////////////////////////////////////// //
19 //Вызовите метод Dispose() для активного освобождения. ресурсы, гибкость
20 FrankClassWithDispose _frankClassWithDispose = null;
21 попытка
двадцать два {
23 _frankClassWithDispose = новый FrankClassWithDispose();
24 _frankClassWithDispose.DoSomething();
25
26}
27 наконец
28 {
29 if (_frankClassWithDispose != null )
30 _frankClassWithDispose.Dispose();
31 // Console.WriteLine("Экземпляр FrankClassWithDispose выпущен");
32}
33
34 /**/ /////////////////////////////////////////// //(2 ) / ///////////////////////////////////////////////
35 // Вы можете использовать оператор using для создания неуправляемого объекта. Прежде чем выполнение метода завершится, он будет вызван.
36 с использованием (FrankClassWithDispose _frankClassWithDispose2 = новый FrankClassWithDispose())
37 {
38 // _frankClassWithDispose2.DoSomething();
39 }
40
41 /**/ ////////////////////////////////////////// //(3 ) / //////////////////////////////////////////// //
42 //При работе сборщика мусора ресурсы освобождаются один раз
43 FrankClassNoFinalize _frankClassNoFinalize = новый FrankClassNoFinalize();
44 _frankClassNoFinalize.DoSomething();
45
46 /**/ ////////////////////////////////////////////// (4) /////////////////////////////////////////////// / /
47 // Когда сборщик мусора работает, для освобождения ресурсов требуется два раза.
48 FrankClassWithDestructor _frankClassWithDestructor = новый FrankClassWithDestructor();
49 _frankClassWithDestructor.DoSomething();
50 /**/ ////////////////////////////////////////////// (5 ) /////////////////////////////////////////////// /
51 // Оператор using нельзя использовать для создания объекта, поскольку он не реализует интерфейс IDispose.
52 // использование (FrankClassWithDestructor _frankClassWithDestructor2 = new FrankClassWithDestructor())
53 // {
54 // _frankClassWithDestructor2.DoSomething();
55 // }
56
57 /**/ ////////////////////////////////////////////// /// ///////////////////////////////////////// //
58 // Для отладки
59 Console.WriteLine( "Нажмите любую клавишу, чтобы продолжить");
60 Консоль.ReadLine();
61
62
63}
64}
65 }
66
Иногда ресурсы должны быть освобождены в определенное время. Класс может реализовать интерфейс IDisposable, который выполняет методы управления ресурсами и очистки IDisposable.Dispose.
Если вызывающему объекту необходимо вызвать метод Dispose для очистки объекта, класс должен реализовать метод Dispose как часть контракта. Сборщик мусора не вызывается по умолчанию
Однако реализация метода Dispose может вызывать методы в сборщике мусора, чтобы регулировать окончательное поведение сборщика мусора.
Стоит отметить, что: вызов метода Dispose() активно освобождает ресурсы и является гибким. Вы можете использовать оператор using для создания неуправляемых объектов. Прежде чем выполнение метода завершится, он будет вызван.
Метод Dispose() освобождает ресурсы. Эффект на обоих концах кода одинаков. Вы можете просмотреть скомпилированный IL.
Код
1. попробовать
2 {
3 IL_0003: нет
4 IL_0004: экземпляр newobj void [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
5 IL_0009: стлок 0.
6 IL_000a: ldloc.
7 IL_000b: экземпляр callvirt void [MemoryManagement]MemoryManagement.FrankClassWithDispose::DoSomething()
8 IL_0010: нет
9 IL_0011: нет
10 IL_0012: оставь.с IL_0028
11 } // конец .try
12 наконец
13 {
14 IL_0014: нет
15 IL_0015: ldloc.
16 IL_0016: ldnull
17 IL_0017: чек
18 IL_0019: stloc.s CS$ 4 $ 0000
19 IL_001b: ldloc.s CS$ 4 $ 0000
20 IL_001d: brtrue.s IL_0026
21 IL_001f: ldloc.
22 IL_0020: экземпляр callvirt void [MemoryManagement]MemoryManagement.FrankClassWithDispose::Dispose()
23 IL_0025: нет
24 IL_0026: нет
25 IL_0027: конец наконец
26 } // обработчик завершения
27 IL_0028: нет
28 IL_0029: экземпляр newobj void [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
29 IL_002e: стлок 1.
30. попробовать
31 {
32 IL_002f: нет
33 IL_0030: нет
34 IL_0031: уйти.с IL_0045
35 } // конец .try
36 наконец
37 {
38 IL_0033: лдлок.1.
39 IL_0034: ldnull
40 IL_0035: чек
41 IL_0037: stloc.s CS$ 4 $ 0000
42 IL_0039: ldloc.s CS$ 4 $ 0000
43 IL_003b: brtrue.s IL_0044
44 IL_003d: ldloc.1
45 IL_003e: экземпляр callvirt void [mscorlib]System.IDisposable::Dispose()
46 IL_0043: нет
47 IL_0044: конец наконец
48 } // обработчик завершения
49
Оператор using имеет тот же эффект для освобождения ресурсов неуправляемого объекта. Это часто встречается в интервью, например, как используется ключевое слово «Использование» и подобные вопросы. Основной идеальный ответ заключается в том, что в дополнение к ссылке на пространство имен и установке псевдонимов для пространства имен такое использование реализует переработку ресурсов неуправляемых объектов, таких как блок tryfinally. Просто простой способ написать это.
Когда вы используете метод Dispose для освобождения неуправляемого объекта, вам следует вызвать GC.SuppressFinalize. Если объект находится в очереди финализации, GC.SuppressFinalize не позволит сборщику мусора вызвать метод Finalize. Потому что вызов метода Finalize пожертвует некоторой производительностью. Если ваш метод Dispose уже очистил делегированные ресурсы, сборщику мусора нет необходимости снова вызывать метод Finalize объекта (MSDN). Для справки прилагается код MSDN.
Код
общедоступный класс BaseResource: IDisposable
{
//Указать на внешние неуправляемые ресурсы
частный дескриптор IntPtr;
// Другие управляемые ресурсы, используемые этим классом.
частные компоненты компонентов;
// Отслеживаем, вызывается ли метод .Dispose, бит флага, управляем поведением сборщика мусора
частный bool расположен = ложь;
//Конструктор
публичный базовый ресурс()
{
// Вставьте сюда соответствующий код конструктора.
}
// Реализуем интерфейс IDisposable.
// Невозможно объявить виртуальный метод virtual.
// Подклассы не могут переопределить этот метод.
публичная недействительность Dispose()
{
Удалить (правда);
//Выходим из очереди финализации
//Устанавливаем код финализатора блокировки объекта
//
GC.SuppressFinalize(это);
}
// Dispose(bool dispositing) выполняется в двух разных ситуациях.
// Если disposition равен true, метод был вызван
// Или косвенно вызывается кодом пользователя. Можно высвободить как управляемый, так и неуправляемый код.
// Если расположение равно false, метод был вызван финализатором внутри,
// Вы не можете ссылаться на другие объекты, можно освободить только неуправляемые ресурсы.
защищенная виртуальная пустота Dispose (удаление bool)
{
// Проверяем, был ли вызван Dispose.
если ( ! это .расположено)
{
// Если равно true, освобождаем все управляемые и неуправляемые ресурсы
если (удалить)
{
// Освободить управляемые ресурсы.
Компоненты.Dispose();
}
// Освобождаем неуправляемые ресурсы, если удаление имеет значение false,
// Будет выполнен только следующий код.
CloseHandle(дескриптор);
дескриптор = IntPtr.Zero;
// Обратите внимание, что это не потокобезопасно.
// После освобождения управляемого ресурса можно запустить другие потоки для уничтожения объекта.
// Но до того, как для установленного флага будет установлено значение true
// Если требуется потокобезопасность, клиент должен ее реализовать.
}
расположен = правда;
}
//Используем взаимодействие для вызова метода
// Очистить неуправляемые ресурсы.
[System.Runtime.InteropServices.DllImport(" Kernel32 " )]
частный extern static Boolean CloseHandle (дескриптор IntPtr);
//Используем деструктор C# для реализации кода финализатора
// Это можно вызвать и выполнить только в том случае, если метод Dispose не был вызван.
// Если вы дадите базовому классу возможность завершиться.
// Не предоставляйте деструктор для подклассов.
~БазовыйРесурс()
{
// Не создавать заново код очистки.
// Из соображений надежности и удобства обслуживания вызов Dispose(false) является лучшим способом
Удалить (ложь);
}
// Позволяет вызывать метод Dispose несколько раз,
// Но если объект был освобожден, будет сгенерировано исключение.
// Независимо от того, когда вы обрабатываете объект, вы проверяете, освобожден ли объект.
// проверяем, удален ли он.
публичная недействительность DoSomething()
{
если (это .dispose)
{
выбросить новое ObjectDisposeException();
}
}
Для типов, для которых вызов метода Close более естественен, чем метод Dispose, вы можете добавить метод Close в базовый класс.
Метод Close не принимает параметров и вызывает метод Dispose, который выполняет соответствующую работу по очистке.
В следующем примере демонстрируется метод Close.
// Не устанавливайте виртуальный метод.
// Унаследованным классам не разрешено переопределять этот метод
публичная недействительность Закрыть()
{
// Вызов параметра Dispose без параметров.
Удалить();
}
общественная статическая пустота Main()
{
//Вставьте сюда код для создания
// и используем объект BaseResource.
}