От монолитных структур до мира распределенных систем разработка приложений прошла долгий путь. Массовое внедрение облачных вычислений и микросервисной архитектуры существенно изменило подход к созданию и развертыванию серверных приложений. Вместо гигантских серверов приложений теперь у нас есть независимые, индивидуально развернутые сервисы, которые начинают действовать
по мере необходимости.
Однако новым игроком в блоке, который может повлиять на это бесперебойное функционирование, может быть «холодный старт». Холодный старт срабатывает, когда первый запрос обрабатывается на только что созданном работнике. Эта ситуация требует инициализации среды выполнения языка и инициализации конфигурации службы перед обработкой фактического запроса. Непредсказуемость и более медленное выполнение, связанные с холодным запуском, могут нарушить соглашения об уровне обслуживания облачной службы. Итак, как можно противостоять этому растущему беспокойству?
Для борьбы с неэффективностью холодного запуска был разработан новый подход, включающий анализ точек на, инициализацию приложения во время сборки, создание моментальных снимков кучи и упреждающую (AOT) компиляцию. Этот метод работает в предположении закрытого мира, требуя, чтобы все классы Java были предопределены и доступны во время сборки. На этом этапе комплексный анализ точек определяет все доступные элементы программы (классы, методы, поля), чтобы гарантировать, что компилируются только необходимые методы Java.
Код инициализации приложения может выполняться в процессе сборки, а не во время выполнения. Это позволяет предварительно выделять объекты Java и создавать сложные структуры данных, которые затем становятся доступными во время выполнения через «кучу изображений». Эта куча изображений интегрирована в исполняемый файл, обеспечивая немедленную доступность при запуске приложения.
итеративное выполнение анализа точек и создания снимков продолжается до тех пор, пока не будет достигнуто стабильное состояние (фиксированная точка), оптимизируя как время запуска, так и потребление ресурсов.
Вводными данными для нашей системы является байт-код Java, который может быть взят из таких языков, как Java, Scala или Kotlin. В этом процессе приложение, его библиотеки, JDK и компоненты виртуальной машины обрабатываются единообразно для создания собственного исполняемого файла, специфичного для операционной системы и архитектуры, называемого «собственным образом». Процесс построения включает в себя итеративный анализ точек и создание снимков кучи до тех пор, пока не будет достигнута фиксированная точка, что позволяет приложению активно участвовать посредством зарегистрированных обратных вызовов. Эти шаги вместе называются процессом создания собственного образа (Рис. 1)
Рис. 1. Процесс создания собственного образа (источник: redhat.com)
Мы используем точечный анализ, чтобы убедиться в достижимости классов, методов и полей во время выполнения. Анализ точек к начинается со всех точек входа, таких как основной метод приложения, и итеративно обходит все транзитивно достижимые методы, пока не достигнет фиксированной точки (Рисунок 2).
Рис. 2. Объекты для анализа
Наш точечный анализ использует интерфейс нашего компилятора для анализа байт-кода Java в высокоуровневом промежуточном представлении компилятора (IR). Впоследствии IR преобразуется в граф типа потока. В этом графе узлы представляют инструкции, работающие с типами объектов, а ребра обозначают ребра направленного использования между узлами, указывающие от определения к использованию. Каждый узел поддерживает состояние типа, состоящее из списка типов, которые могут достигать узла, и информации о недействительности. Состояния типов распространяются через ребра использования; если состояние типа узла изменяется, это изменение распространяется на все способы использования. Важно отметить, что состояния типов могут только расширяться; новые типы могут быть добавлены в состояние типа, но существующие типы никогда не удаляются. Этот механизм гарантирует, что
анализ в конечном итоге сходится к фиксированной точке, что приводит к завершению.
Анализ точек на направляет выполнение кода инициализации, когда он достигает локальной фиксированной точки. Этот код берет свое начало в двух отдельных источниках: инициализаторах классов и пакете пользовательского кода, выполняемом во время сборки через функциональный интерфейс:
Инициализаторы классов: Каждый класс Java может иметь инициализатор класса, указанный методом
Явные обратные вызовы: Разработчики могут реализовывать собственный код с помощью перехватчиков, предоставляемых нашей системой, выполняющихся до, во время или после этапов анализа.
Вот API-интерфейсы, предоставляемые для интеграции с нашей системой.
boolean isReachable(Class> clazz); boolean isReachable(Field field); boolean isReachable(Executable method);
Для получения дополнительной информации обратитесь к QueryReachabilityAccess
void registerReachabilityHandler(Consumercallback, Object... elements); void registerSubtypeReachabilityHandler(BiConsumer > callback, Class> baseClass); void registerMethodOverrideReachabilityHandler(BiConsumer callback, Executable baseMethod);
Для получения дополнительной информации см. раздел BeforeAnalysisAccess
На этом этапе приложение может выполнять собственный код, например выделение объектов и инициализацию более крупных структур данных. Важно отметить, что код инициализации может получить доступ к текущему состоянию анализа точек, позволяя запрашивать доступность типов, методов или полей. Это достигается с помощью различных методов isReachable(), предоставляемых во время анализаAccess. Используя эту информацию, приложение может создавать структуры данных, оптимизированные для доступных сегментов приложения.
Наконец, создание снимков кучи создает граф объектов, следуя корневым указателям, таким как статические поля, для создания комплексного представления всех доступных объектов. Затем этот график заполняет исходное изображение
куча изображений, обеспечивающая эффективную загрузку исходного состояния приложения при запуске.
Чтобы сгенерировать транзитивное замыкание достижимых объектов, алгоритм обходит поля объекта, считывая их значения с помощью отражения. Очень важно отметить, что построитель образов работает в среде Java. Во время этого обхода учитываются только поля экземпляров, помеченные как «прочитанные» при анализе точек на. Например, если класс имеет два поля экземпляра, но одно из них не помечено как прочитанное, объект, доступный через неотмеченное поле, исключается из кучи изображений.
При обнаружении значения поля, класс которого ранее не был идентифицирован анализом точек на, этот класс регистрируется как тип поля. Эта регистрация гарантирует, что в последующих итерациях анализа точек новый тип будет распространяться на все чтения полей и транзитивные использования в графе потока типов.
Приведенный ниже фрагмент кода описывает основной алгоритм создания моментальных снимков кучи:
Declare List worklist := [] Declare Set reachableObjects := [] Function BuildHeapSnapshot(PointsToState pointsToState) For Each field in pointsToState.getReachableStaticObjectFields() Call AddObjectToWorkList(field.readValue()) End For For Each method in pointsToState.getReachableMethods() For Each constant in method.embeddedConstants() Call AddObjectToWorkList(constant) End For End For While worklist.isNotEmpty Object current := Pop from worklist If current Object is an Array For Each value in current Call AddObjectToWorkList(value) Add current.getClass() to pointsToState.getObjectArrayTypes() End For Else For Each field in pointsToState.getReachableInstanceObjectFields(current.getClass()) Object value := field.read(current) Call AddObjectToWorkList(value) Add value.getClass() to pointsToState.getFieldValueTypes(field) End For End If End While Return reachableObjects End Function
Подводя итог, можно сказать, что алгоритм создания снимков кучи эффективно создает снимок кучи путем систематического обхода доступных объектов и их полей. Это гарантирует, что в кучу изображения будут включены только соответствующие объекты, оптимизируя производительность и объем памяти исходного образа.
В заключение, процесс создания снимков кучи играет решающую роль в создании собственных образов. Систематически просматривая доступные объекты и их поля, алгоритм создания снимков кучи создает граф объектов, который представляет собой транзитивное замыкание доступных объектов из корневых указателей, таких как статические поля. Этот граф объектов затем встраивается в собственное изображение в виде кучи изображений, служащей начальной кучей при запуске собственного образа.
На протяжении всего процесса алгоритм опирается на состояние анализа точек, чтобы определить, какие объекты и поля подходят для включения в кучу изображений. Объекты и поля, помеченные как «прочитанные» при анализе точек на, учитываются, а неотмеченные сущности исключаются. Кроме того, при обнаружении ранее неизвестных типов алгоритм регистрирует их для распространения в последующих итерациях анализа точек.
В целом, создание снимков кучи оптимизирует производительность и использование памяти собственными изображениями, гарантируя, что в кучу изображений включаются только необходимые объекты. Такой системный подход повышает эффективность и надежность выполнения собственных изображений.
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3