Глава.21 .Автоматическое.управление.памятью.(уборка.мусора)
на финализацию), указатель считается относящимся к недоступному объекту
(мусором) и приравнивается к
null
.
5. Уборщик мусора сжимает память, убирая свободные места, оставшиеся на месте
недоступных объектов. Учтите, что уборщик иногда предпочитает не сжимать
память, если посчитает, что фрагментация низка и на ее устранение не стоит
тратить время. Объекты с флагом
Pinned
не сжимаются (не перемещаются),
а объекты, находящиеся рядом, могут перемещаться.
Разобравшись в логике работы уборщика, попробуем использовать эти знания
на практике. Наиболее понятны флаги
Normal
и
Pinned
, поэтому начнем с них.
Обычно они применяются при взаимодействии с неуправляемым кодом.
Флаг
Normal
используется для передачи ссылки на управляемый объект не-
управляемому коду при условии, что позже неуправляемый код выполнит обратный
вызов управляемого кода, передав ему эту ссылку. В общем случае невозможно
передать ссылку на управляемый объект неуправляемому коду, потому что при
уборке мусора адрес объекта в памяти может измениться, и указатель станет не-
действительным. Чтобы решить эту проблему, можно вызвать метод
Alloc
объекта
GCHandle
и передать ему ссылку на объект и флаг
Normal
. Затем возвращенный
экземпляр
GCHandle
нужно привести к типу
IntPtr
и передать полученный ре-
зультат в неуправляемый код. Когда неуправляемый код выполнит обратный
вызов управляемого кода, последний приведет передаваемый тип
IntPtr
обратно
к
GCHandle
, после чего запросит свойство
Target
, чтобы получить ссылку на управ-
ляемый объект (или его текущий адрес). Когда неуправляемому коду эта ссылка
будет больше не нужна, следует вызывать метод
Free
объекта
GCHandle
, который
позволит очистить объект при следующей уборке мусора (при условии, что для
этого объекта нет других корней).
Обратите внимание, что в этой ситуации неуправляемый код не работает с управ-
ляемым объектом как таковым, а лишь использует возможность сослаться на него.
Однако бывают ситуации, когда неуправляемому коду требуется управляемый объ-
ект. Тогда этот объект следует отметить флагом
Pinned
— это запретит уборщику
мусора перемещать и сжимать его. Типичный пример — передача управляемого
объекта
String
функции Win32. При этом объект
String
надо обязательно отме-
тить флагом
Pinned
, потому что нельзя передать ссылку на управляемый объект
неуправляемому коду из-за возможного перемещения этого объекта уборщиком
мусора. В противном случае, если бы объект
String
был перемещен, неуправляе-
мый код выполнял бы чтение из памяти или запись в память, более не содержащую
символов объекта
String
, и работа приложения стала бы непредсказуемой.
При использовании для вызова метода механизма P/Invoke CLR автоматически
устанавливает для аргументов флаг
Pinned
и сбрасывает его, когда неуправляемый
метод возвращает управление. Поэтому чаще всего в типе
GCHandle
не приходится
самостоятельно явно устанавливать флаг
Pinned
для каких-либо управляемых
объектов. Тип
GCHandle
нужно явно использовать для передачи адреса управля-
емого объекта неуправляемому коду. Затем неуправляемая функция возвращает
601
Мониторинг.и.контроль.времени.жизни.объектов
управление, а неуправляемому коду этот объект может потребоваться позднее.
Чаще всего такая ситуация возникает при асинхронных операциях ввода-вывода.
Допустим, вы выделяете память для байтового массива, который должен запол-
няться данными по мере их поступления из сокета. Затем вызывается метод
Alloc
типа
GCHandle
, передающий ссылку на массив и флаг
Pinned
. Далее при помощи
возвращенного экземпляра
GCHandle
вызывается метод
AddrOfPinnedObject
. Он
возвращает
IntPtr
— действительный адрес объекта с флагом
Pinned
в управляемой
куче. Затем этот адрес передается неуправляемой функции, которая сразу передает
управление управляемому коду. В процессе поступления данных из сокета буфер
байтового массива не должен перемещаться в памяти, что и обеспечивается флагом
Pinned
. По завершении операции асинхронного ввода-вывода вызывается метод
Free
объекта
GCHandle
, который разрешает перемещения в буфер при следующей
уборке мусора. В управляемом коде по-прежнему должна присутствовать ссылка
на этот буфер, обеспечивая разработчику доступ к данным. Эта ссылка не позволит
уборщику полностью убрать буфер из памяти.
Следует упомянуть и о таком средстве фиксации объектов внутри кода, как
инструкция
fixed
языка C#. Вот пример ее применения:
unsafe public static void Go() {
// Выделение места под объекты, которые немедленно превращаются в мусор
for (Int32 x = 0; x < 10000; x++) new Object();
IntPtr originalMemoryAddress;
Byte[] bytes = new Byte[1000]; // Располагаем этот массив
// после мусорных объектов
// Получаем адрес в памяти массива Byte[]
fixed (Byte* pbytes = bytes) { originalMemoryAddress = (IntPtr) pbytes; }
// Принудительная уборка мусора
// Мусор исчезает, позволяя сжать массив Byte[]
GC.Collect();
// Повторное получение адреса массива Byte[] в памяти
// и сравнение двух адресов
fixed (Byte* pbytes = bytes) {
Console.WriteLine("The Byte[] did{0} move during the GC",
(originalMemoryAddress == (IntPtr) pbytes) ? " not" : null);
}
}
Инструкция
fixed
языка C# работает эффективней, чем выделение в памяти
фиксированного GC-дескриптора. В данном случае она заставляет установить
специальный «блокирующий» флаг на локальную переменную
pbytes
. Уборщик
мусора, исследуя содержимое этого корня и обнаруживая отличные от
null
значе-
ния, понимает, что во время сжатия перемещать объект, на который ссылается эта
переменная, нельзя. Компилятор C# создает IL-код, присваивающий локальной
602
Do'stlaringiz bilan baham: |