1. Когда приложение осуществило JIT-компиляцию и был выполнен большой объем кода, который в ближайшее время не будет вновь выполняться. Довольно часто ситуации подобного рода возникают в тех случаях, когда выполнение приложения происходит в несколько стадий. Вполне возможно, что код, запускаемый на начальной стадии выполнения приложения для инициализации или настройки объектов и данных, впоследствии повторно выполняться не будет. Если возникает необходимость в дополнительном распределении памяти, то имеется полный смысл использовать для этого память, в которой хранится данный код.
2. Когда набор хранящихся в памяти активных объектов становится настолько большим, что возникает риск аварийного завершения выполнения приложения, если для размещения дополнительных объектов, которые в соответствии с алгоритмом должны создаваться в ходе нормального выполнения приложения, не найдется достаточно большого объема свободной памяти. В этом случае исполнительный механизм будет вынужден вытеснять из памяти и периодически перекомпилировать необходимый приложению код лишь для того, чтобы приложение было в состоянии продолжать дальнейшее выполнение. Если другой возможной альтернативой является только прекращение выполнения приложения из-за нехватки памяти, то единственным решением может быть лишь очистка памяти от JIT-компилированного кода, даже если это означает дополнительные затраты времени на последующую повторную компиляцию этого кода.
Полезно разобраться в том, каким образом исполнительный механизм взаимодействует с кодом и объектами приложения в процессе его выполнения.
Приведенные ниже схематические диаграммы помогут нам проследить, как осуществляется управление памятью на различных стадиях выполнения приложения.
Рис. 3.2.
Таблица 3.1. Условные графические обозначения, используемые на рис. 3.2, 3.3 и остальных рисунках
Фигура | Описание |
---|---|
Темно-серые овалы | Используемые в настоящее время объекты (например, объект Obj12 на рис. 3.2). |
Белые овалы | Пассивные объекты. Эти объекты (например, объект Obj6 на рис. 3.2) являются "мусором", к которому у приложения больше нет доступа и от которого можно избавиться с целью высвобождения дополнительной памяти. |
Темно-серые прямоугольники | Описания классов и типов, которые были загружены в память по той причине, что к типам осуществлял доступ выполняющийся код. |
Внутренние прямоугольники | Код, содержащийся в классах. (Код целиком находится внутри класса.) |
Темно-серые внутренние прямоугольники | Код, подвергнутый JIТ-компиляции, поскольку метод был вызван хотя бы один раз (например, код Code1 класса ClassXYZ на рис.3.2). |
Светло-серые внутренние прямоугольники | Код, который еще не подвергался JIТ-компиляции. По сравнению с прямоугольниками JIТ-компилированного кода эти прямоугольники имеют меньшие размеры, ибо еще не скомпилированные методы (например, метод Code4 класса XYZ на рис. 3.2) занимают в памяти очень мало места. |
Приведенную на рис. 3.2 схему можно прокомментировать следующим образом:
■ Размеры отдельных элементов схемы выбраны таким образом, чтобы они давали представление об относительных объемах используемой памяти. Визуально большие классы и типы используют больше памяти по сравнению с визуально меньшими, поскольку содержат больше "начинки". Объекты различных типов используют различные объемы памяти. Пассивные объекты занимают память до тех пор, пока не будет выполнена операция сборки мусора. Методы, не подвергавшиеся JIТ-компиляции, занимают в памяти очень мало места.