Книга: Программирование мобильных устройств на платформе .NET Compact Framework

Управление памятью и сборка мусора

В процессе своего обычного выполнения ваш код периодически инициирует загрузку определений типов в память, компиляцию и выполнение кода, а также размещение объектов в памяти и их удаление из памяти. .NET Compact Framework проектировалась таким образом, чтобы обеспечить наилучший баланс между скоростью запуска, устойчивостью и непрерывностью выполнения кода даже в условиях существования жестких ограничений на объем доступной памяти. Для достижения этой цели загрузчик классов, механизм JIT-компиляции, а также модули распределения памяти и сборки мусора работают скоординированным образом.

Вероятнее всего, выполнение вашего кода будет сопровождаться периодическим созданием новых объектов и их последующим уничтожением после того, как необходимость в них исчезает. Эффективное удовлетворение запросов памяти для размещения объектов и ее освобождение достигаются в .NET Compact Framework за счет утилизации освобождаемой памяти путем так называемой "сборки мусора" (garbage collection). Операции по сборке мусора в той или иной форме используются в большинстве современных сред выполнения управляемого кода.

Сборка мусора в основном предназначена для решения двух задач: 1) восстановления памяти, которая больше не используется, и 2) уплотнения участков используемой памяти таким образом, чтобы для вновь распределяемых областей памяти были доступны как можно более крупные блоки. Тем самым решается одна из наиболее распространенных проблем, которая называется фрагментацией памяти. По своей сути эта проблема аналогична проблеме фрагментации дискового пространства. Если дисковое пространство периодически не реорганизовывать, то по прошествии некоторого времени оно дезорганизуется и разобьется на ряд чередующихся свободных и занятых участков небольшого размера; эти участки и являются фрагментами. Можно считать, что здесь мы имеем дело с проявлением действия закона увеличения энтропии применительно к дисковому пространству и памяти; для того, чтобы обратить энтропийные эффекты, необходимо затратить определенную работу. В результате фрагментации доступ к данным замедляется, поскольку в процессе чтения файлов головкам приходится многократно перемещаться вдоль поверхности диска. Фрагментация памяти оборачивается еще худшими бедами, так как для размещения объектов требуются достаточно большие непрерывные блоки памяти, способные уместить целиком все данные, относящиеся к объекту. После выполнения многочисленных операций выделения и освобождения порций памяти различного размера свободные области оказываются расположенными вразброс, а их размеры являются недостаточными, чтобы обеспечивалось наличие больших непрерывных участков памяти, необходимых для размещения объектов. Уплотнение всей используемой, но расположенной вразброс памяти позволяет создавать крупные блоки свободной памяти, которую можно эффективно использовать при распределении памяти для вновь создаваемых объектов.

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

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

Однако в рукаве у .NET Compact Framework наряду с JIT-компилятором, диспетчером памяти и сборщиком мусора спрятан еще один козырь: возможность очищать память от пассивного (dead) JIT-компилированного кода. Обычно это делать нежелательно, но при определенных обстоятельствах такая возможность становится незаменимой. Если уж исполнительный механизм, обеспечивая максимальную производительность, потрудился и выполнил JIT-компиляцию управляемого кода до собственных команд процессора, то отбрасывание JIT-компилированного кода могло бы показаться расточительством, поскольку когда в следующий раз возникнет необходимость в выполнении этого кода, то потребуется его повторная JIT-компиляция. В то же время, наличие такой возможности оборачивается благом в следующих двух случаях:

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

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

Оглавление книги


Генерация: 5.257. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз