从整体结构到分布式系统世界,应用程序开发已经走过了漫长的道路。云计算和微服务架构的大规模使用极大地改变了服务器应用程序的创建和部署。我们现在有一个独立和单独的服务,可以立即投入使用,而不是一个巨大的应用服务器 在需要时。
然而,影响该块平稳运行的新玩家可能是“冷启动”。当第一个要求在新生成的工作线程上处理时,冷启动将启动。在处理实际要求之前,语言操作和服务配置需要初始化。与冷启动相关的不可预测性和缓慢执行速度可能违反云服务的服务水平协议。那么,如何应对这种日益增长的担忧呢?
本机图像:优化启动时间和内存占用为了解决冷启动的低效率问题,我们开发了一种涉及点分析和构建时应用程序初始化、堆放快照和提前的新方法 (aot) 编译。这种方法在封闭世界的假设下运行,要求一切 java 类别在构建过程中已提前确定并可访问。在这个阶段,对所有可访问的程序元素(类别、方法和字段)进行了全面的点分析和确定,以确保只需要编译 java 方法。
在构建过程中可以执行应用程序的初始化代码,而不是在运行过程中。允许提前分配 java 对象并构建复杂的数据结构,然后在运行过程中通过“图像堆”提供数据结构。可执行文件中集成图像,在应用程序启动时提供即时可用性。 执行点分析和快照不断迭代,直到达到稳定状态(定点),从而优化启动时间和资源消耗。
详细的工作流程我们系统的输入是 java 它可能来自字节码 java、scala 或 kotlin 等语言。该过程统一处理应用程序,其库,jdk 和 vm 本机可执行文件生成特定于操作系统和系统结构的组件 - 被称为“本机图像”。构建过程包括迭代点分析和堆叠快照,直到达到固定点,允许应用程序通过注册回调积极参与。这些步骤统称为本机图像构建过程(图) 1)
图 1 – 本机图像构建过程(来源:redhat.com)
点分析我们使用点分析来确定操作时间、方法和字段的可达性。点到分析从所有入口点(如应用程序的主要方法)开始,迭代到固定点(图) 2)。
图 2 – 分析点
我们的方向分析将使用编译器的前端 java 编译器的高级中间表示字节码分析(ir)。随后,ir 转换为类型流图。在此图中,节点表示操作对象类型的指令,同时表示节点之间的定向使用,从定义指向使用。由类型列表和空值信息组成的每个节点维护一种类型状态。通过使用传播类型状态;如果节点的类型状态发生变化,则该变化将传播到所有用途。重要的是,类型状态只能扩展;新类型可以添加到类型状态中,但现有类型永远不会被删除。该机制确保 分析最终收敛到固定点,导致终止。
初始化代码的运行指向分析和指导初始化代码在到达当地固定点时的执行。该代码起源于两个不同的来源:类别初始值设置项和在构建过程中通过功能接口批量执行的自定义代码:
类初始值设定项: 每个 java 可以有一个类的理由
方法指示的类别初始值设置项,该方法初始化静态字段。开发人员可以选择在施工和运行过程中初始化的类别。 显式回调:开发人员可以在分析阶段之前、期间或之后,通过我们系统提供的钩子实现自定义代码。
这里提供了与我们的系统集成的用途 api。
被动api(查询当前分析状态)boolean isreachable(class> clazz); boolean isreachable(field field); boolean isreachable(executable method);
请参考更多信息 queryreachabilityaccess
active api(注册分析状态变更的回调):void registerreachabilityhandler(consumer<duringanalysisaccess> callback, object... elements); void registersubtypereachabilityhandler(biconsumer<duringanalysisaccess class>> callback, class> baseclass); void registermethodoverridereachabilityhandler(biconsumer<duringanalysisaccess executable> callback, executable basemethod); </duringanalysisaccess></duringanalysisaccess></duringanalysisaccess>
更多信息请参考beforeanalysisacesss
在这个阶段,应用程序可以执行自定义代码,如对象分配和大数据结构的初始化。重要的是,初始化代码可以访问当前的分析状态点,从而使用相关类型、方法或字段的可达性。使用duringanalysisacess 提供各种isreachable() 完成方法。利用此信息,应用程序可以构建优化应用程序到达段的数据结构。
堆快照最后,像静态字段一样,堆叠快照跟随根指针构建对象图,以构建所有可访问对象的全面视图。然后图片填充本机图像 图像堆,确保启动时应用程序的初始状态高效加载。
该算法通过反射读取可达对象的值,以生成可达对象的传输封闭包。需要注意的是,图像生成器在 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
综上所述,堆快照算法可以通过系统地遍历可达对象及其字段来有效地构建堆快照。这可以确保图像堆只包含相关对象,从而优化图像的性能和内存占用。
结论总之,堆叠快照的过程在创建原始镜像中起着至关重要的作用。堆叠快照算法通过系统地遍历可达对象及其字段构建了一个对象图,表示可达对象从根指针(如静态字段)传输和关闭。然后将对象图像作为图像堆嵌入机器图像中,作为机器图像启动时的初始堆。
在整个过程中,算法依赖于分析点的状态来确定哪些对象和字段与图像堆中包含的相关性。考虑到点分析标记为“已读”的对象和字段,并排除未标记的实体。此外,当遇到以前从未见过的类型时,算法会注册它们,以便在点分析的后续迭代中传播。
一般来说,堆快照优化了图像的性能和内存使用,以确保图像堆中只包含必要的对象。该系统方法提高了图像执行的效率和可靠性。
以上是通过静态分析、图像初始化和堆放快照来提高性能的详细内容。请关注图灵教育的其他相关文章!