Spring Boot 4.1 + Java AOT Cache:把 Agent 服务冷启动和暖机做成可交付指标 今天聊一个偏 Java 运行时但很适合 Agent 服务的话题AOT Cache。很多 Agent 性能优化只盯模型延迟但线上 p99 抖动经常来自冷 pod、首次请求、类加载、Spring 容器初始化和 JIT 暖机。Spring Boot 4.1 已经把 Java 25 AOT Cache 写进参考文档适合我们把 Agent 冷启动从“看日志感觉快了”变成 CI/CD 里的制品、训练和指标。分享日期2026-07-01主题Java 25 / JDK 26 / Project Leyden / Spring Boot 4.1.0 / AOT Cache / Spring AI 2.0.0 / Spring Cloud 2025.1.2 / Agent Cold Start版本背景截至 2026-07-01Spring Boot 官方项目页显示4.1.0Spring AI 官方项目页显示2.0.0Spring Cloud 官方项目页显示2025.1.2Spring Cloud 兼容矩阵仍标注2025.1.xOakwood 对应 Spring Boot4.0.x真实项目组合应以 BOM、Release Notes 和 start.spring.io 生成结果为准。1. 为什么今天值得关注过去几周我们连续覆盖了 Spring AI 2.0 的 Tool Calling、Tool Search、Chat Memory、Structured Output、多模态、MCP、评估观测和安全补丁 Agent。今天换一个更偏 Java 运行时的热点AOT Cache 如何改善 Spring Boot Agent 服务的冷启动和暖机。很多团队做 Agent 时第一反应是关注模型延迟模型首 token 慢不慢。RAG 向量检索慢不慢。工具调用链路慢不慢。多模态文件上传和转录慢不慢。这些都重要但线上还有一个更基础的延迟来源新的 Java 实例刚启动时本身还没有完成类加载、链接、Spring 容器初始化和 JIT 暖机。在 Kubernetes 自动扩缩容、蓝绿发布、金丝雀发布、Serverless scale-to-zero、抢占式节点迁移等场景里这个问题会直接变成 p99 和 p999 的尾延迟。Agent 服务比普通 CRUD 服务更容易踩中冷启动问题冷启动阶段普通服务影响Agent 服务额外影响JVM 启动类加载、链接、初始化AI SDK、HTTP client、JSON schema、工具定义更多Spring 容器刷新Bean 创建、自动配置ChatClient、Advisor、VectorStore、ToolCallback 初始化首次请求Controller、序列化、数据库连接首次构造 prompt、工具 schema、模型 client、RAG 检索暖机期JIT 尚未达到稳定状态prompt 组装、结构化输出转换、工具执行路径都可能偏慢一句话AOT Cache 不是让大模型回答更快而是让承载 Agent 的 Java/Spring 进程更快进入可服务状态并更快接近稳定性能。2. 版本坐标与官方事实今天这篇分享基于以下官方事实Project LeydenOpenJDK 项目页说明该项目的主要目标是改善 Java 程序的启动时间、达到峰值性能的时间和内存占用。项目页列出 JDK 24 已交付 AOT Class Loading LinkingJDK 25 已交付 AOT Command-Line Ergonomics 与 AOT Method ProfilingJDK 26 已交付 AOT Object Caching with Any GC。JEP 514 / JDK 25-XX:AOTCacheOutputapp.aot简化了创建 AOT cache 的命令把常见训练和缓存创建流程收敛成更易使用的一步。JEP 515 / JDK 25AOT Method Profiling 把训练运行中收集到的方法执行 profile 放入 AOT cache让生产运行更快进入有效 JIT 优化状态。JEP 516 / JDK 26AOT Object Caching with Any GC 让 AOT cache 能与任意垃圾回收器协同包括低延迟 ZGC目标是避免在启动尾延迟和 GC 尾延迟之间二选一。Spring Boot 4.1.0 AOT Cache 文档Spring Boot 支持 Java 25 的 AOT cache如果使用 Java 25 之前的版本需要使用 CDS。文档明确建议在可行时优先使用 AOT cache。Spring Boot JVM AOT 文档Spring 的 AOT 生成初始化代码可以和 JVM AOT cache 组合使用但它有固定 classpath、运行时 Bean 图不能变化等限制。Spring AI 2.0.0官方项目页列出 Chat、Embedding、Structured Outputs、Tool Calling、Observability、Evaluation、Chat Memory、RAG、Spring Boot auto-configuration 和 starters 等能力。这决定了今天的边界本文讨论的是 Java/Spring Agent 服务的启动与暖机治理不讨论模型推理本身的加速。模型调用、向量库、工具服务和网络仍然要用各自的缓存、限流、熔断和观测手段治理。3. AOT Cache、Spring AOT、Native Image、CRaC/CDS 怎么区分先把几个容易混淆的概念拆开。技术解决什么适合 Agent 场景主要代价JVM AOT Cache加快类加载、链接和暖机需要保留 JVM/JIT 动态能力同时改善冷启动需要训练运行缓存和 Java 版本、应用版本绑定Spring AOT生成 Spring 初始化代码减少运行时推断Bean 图稳定、配置可在构建/训练前确定的服务classpath 固定运行时 profile/conditional bean 有限制GraalVM Native Image极低冷启动、更小内存极端冷启动、Serverless、边缘部署构建更重反射/动态代理/资源配置更严格CRaC从 checkpoint 恢复运行时状态想保存完整已启动状态的服务连接、线程、外部资源恢复语义复杂CDSJava 25 前的类数据共享方案旧 JDK 或过渡方案能力比 AOT cache 窄Java 25 优先 AOT cache对多数 Spring Boot Agent 服务来说务实路线是先测普通 JAR 冷启动和首请求延迟。再使用 JVM AOT Cache。如果 Bean 图稳定再叠加 Spring AOT。如果仍有极端冷启动诉求再评估 Native Image 或 CRaC。不要一上来就把所有 Agent 都改成 Native Image。Agent 服务经常接入第三方 SDK、模型客户端、动态工具、JSON schema、MCP client、向量库和 observability agent运行时动态性比普通服务更强。AOT Cache 的价值在于它先保留 JVM 运行模型同时改善冷启动和暖机。4. Spring Boot 4.1 的最小 AOT Cache 流程Spring Boot 文档给出的核心流程是先把应用解包成 extracted form再做训练运行生成 cache最后启动时加载 cache。java -Djarmodetools -jar my-app.jar extract --destination application cd application java -XX:AOTCacheOutputapp.aot -Dspring.context.exitonRefresh -jar my-app.jar java -XX:AOTCacheapp.aot -jar my-app.jar几个关键点AOT cache 需要和 extracted form 一起使用否则没有效果。cache 文件只有在应用未更新且 Java 版本相同的情况下才能复用。-Dspring.context.exitonRefresh适合生成只覆盖容器刷新阶段的低风险启动 cache。如果想覆盖更多业务热路径可以在训练运行里执行受控 smoke traffic但必须避免外部副作用。对 Agent 服务来说可以把训练分成两档训练档位训练内容适用场景启动档Spring 容器刷新、自动配置、Actuator、ChatClient Bean、工具 Bean 初始化第一阶段低风险、容易标准化暖机档调用只读 mock 模型、mock vector store、工具 schema 构造、结构化输出转换需要进一步降低首请求延迟第一版建议先做启动档。它能覆盖大部分 class loading、Spring Bean 初始化、自动配置和基础库路径且不会碰真实模型和业务工具。5. Agent 服务训练运行不能乱跑AOT cache 依赖训练运行。训练运行越接近生产cache 越有价值但训练运行一旦触发真实副作用就会制造新风险。Agent 服务训练运行要避开这些动作调真实大模型产生费用。调真实 RAG 向量库扫描大量数据。调真实业务工具创建工单、退款、发短信、发邮件、部署发布。写入生产数据库或消息队列。读取真实敏感用户会话作为训练样本。更稳的训练 profile 可以这样设计spring: profiles: active: aot-training agent: training: enabled: true use-mock-model: true use-mock-vector-store: true allow-write-tools: false warmup-scenarios: - chat-client-build - tool-schema-render - structured-output-conversion - health-and-startup-actuator再加一个只在训练 profile 启用的 warmup runnerComponent Profile(aot-training) class AgentAotWarmupRunner implements ApplicationRunner { private final ChatClient chatClient; private final AgentToolRegistry toolRegistry; private final ObjectMapper objectMapper; AgentAotWarmupRunner(ChatClient.Builder builder, AgentToolRegistry toolRegistry, ObjectMapper objectMapper) { this.chatClient builder.build(); this.toolRegistry toolRegistry; this.objectMapper objectMapper; } Override public void run(ApplicationArguments args) throws Exception { toolRegistry.describeReadOnlyTools(); var sample new AgentWarmupResult( READY, List.of(mock citation), false); objectMapper.writeValueAsString(sample); chatClient.prompt() .system(Warm up the agent runtime with mock dependencies only.) .user(Return a small readiness answer.) .call() .content(); } } record AgentWarmupResult( String decision, ListString citations, boolean requiresHumanReview) { }这个 runner 的目标不是验证模型质量而是让关键类、schema、序列化、advisor、tool callback 等路径在训练运行中出现。真实质量评估仍然应该交给评估集和 CI。6. Spring AOT 可以叠加但要尊重限制Spring Boot 的 JVM AOT 文档说明使用 Spring AOT 生成的初始化代码有利于启动时间AOT cache 和 Spring AOT 可以组合来进一步改善启动。但 Spring AOT 有明确限制classpath 在构建时固定。应用里的 Bean 定义不能在运行时变化。Profile和 profile-specific 配置有局限。会影响 Bean 创建的属性条件例如某些ConditionalOnProperty或.enabled属性不适合作为运行时切换 Bean 图的手段。这对 Agent 服务有直接影响。很多 Agent 原型会把模型、工具和 advisor 做成“运行时随便开关”agent: tools: refund: enabled: false shipment: enabled: true models: active: openai如果这些开关会改变 Bean 是否存在就不适合在 Spring AOT 运行时随意切。更稳的设计是构建/训练前确定 Bean 图。运行时开关只控制策略不控制 Bean 存不存在。工具授权通过服务端 policy 判断而不是动态注册/注销 Bean。每个部署 profile 生成自己的 AOT cache不跨 profile 复用。示例record AgentRuntimePolicy( SetString enabledToolNames, SetString writeToolApprovalRequired, String activeModelProfile) { boolean canExposeTool(String toolName) { return enabledToolNames.contains(toolName); } }工具 Bean 可以稳定存在但是否对某个租户、用户、会话可见由 policy 决定。这样更适合 AOT也更适合安全审计。7. 容器镜像里怎么放 app.aotAOT cache 的产物应该被当成构建制品而不是容器启动时临时生成。一个简化的镜像结构可以这样理解/workspace/application/ my-app.jar BOOT-INF/ org/ app.aot启动命令java -XX:AOTCacheapp.aot -jar my-app.jarCI/CD 里要把这些信息固化成制品元数据{ artifact: order-agent-api, applicationVersion: 2026.07.01.1, javaVersion: 25.0.2, springBootVersion: 4.1.0, springAiVersion: 2.0.0, aotCache: app.aot, trainingProfile: aot-training, classpathHash: sha256:..., trainingScenarioVersion: 2026-07-01 }如果应用 JAR、依赖、Java 版本、训练 profile、Spring profile 变化就应该重新生成 cache。不要把app.aot当成可以跨版本共享的“通用加速包”。8. Kubernetes 和 Spring Cloud 场景启动快不等于可接流量Agent 服务启动更快以后还要做好流量治理。因为“JVM 进程起来了”和“Agent 能稳定处理请求”不是同一件事。推荐的就绪条件条件检查什么JVM/Spring ready应用上下文刷新完成Actuator health readyAgent runtime readyChatClient、Advisor、ToolRegistry、VectorStore client 初始化完成外部依赖 ready模型 provider、向量库、工具服务、配置中心连接可用warmup ready训练路径或启动后 warmup 完成policy ready工具权限、租户策略、模型路由配置加载完成Spring Cloud 在这里的价值不是“帮你生成 AOT cache”而是负责分布式治理Spring Cloud 能力在 AOT Cache Agent 服务中的作用Gateway只把流量导到 ready 的 Agent 实例做限流、鉴权和灰度Config管理非 Bean 图级别的 runtime policy避免运行时改变 AOT Bean 图Circuit Breaker模型 provider、vector store、tool service 不稳定时隔离故障Kubernetesreadiness/liveness、滚动发布、HPA、PodDisruptionBudgetStream / Task触发离线预热、训练报告、冷启动基线采集Contract升级 cache、JDK、Boot、AI starter 后验证跨服务 API版本上仍要注意Spring Cloud 项目页显示当前入口2025.1.2但兼容矩阵写的是2025.1.xOakwood 对应 Boot4.0.x。如果 Agent 服务使用 Boot4.1.x要以 BOM、release notes 和实际构建验证为准不要只按“最新版本”硬拼。9. 冷启动 SLO不要只看本地启动日志AOT Cache 落地是否有效不能只看本地控制台“快了几秒”。建议把冷启动做成可观测指标。第一组是 Spring/JVM 指标进程启动到ApplicationStartedEvent。进程启动到ApplicationReadyEvent。Actuator/health/readiness首次通过时间。AOT cache 是否启用。训练 cache 版本与应用版本是否匹配。第二组是 Agent 指标首次ChatClient调用前的准备耗时。首次工具 schema 渲染耗时。首次 structured output 转换耗时。首次 RAG advisor 初始化耗时。首次请求的 time-to-first-token。可以加一个很简单的启动观测Component class AgentStartupEvents { private final MeterRegistry meterRegistry; private final long processStartedAt System.nanoTime(); AgentStartupEvents(MeterRegistry meterRegistry) { this.meterRegistry meterRegistry; } EventListener(ApplicationReadyEvent.class) void onReady() { long readyNanos System.nanoTime() - processStartedAt; meterRegistry.timer(agent.startup.ready) .record(readyNanos, TimeUnit.NANOSECONDS); } }再用一个只读 warmup endpoint 或内部 runner 标记 Agent readinessrecord AgentWarmupReport( boolean chatClientReady, boolean toolsReady, boolean vectorStoreReady, Duration duration) { }目标不是追求单个数字而是给发布、扩缩容和回滚提供基线普通 JAR 冷启动 p95 是多少。AOT cache 后冷启动 p95 是多少。Spring AOT AOT cache 后 p95 是多少。首次 Agent 请求 p95 是否下降。内存占用是否变化。出错时是否能自动回退到无 cache 启动。10. CI/CD 流水线建议一个适合 Agent 服务的流水线可以这样拆Flowchart LR Build[Build JAR] -- Extract[Extract Boot App] Extract -- Train[Training Run] Train -- Cache[Create app.aot] Cache -- Test[Start With AOT Cache] Test -- Eval[Agent Smoke Evaluation] Eval -- Image[Build OCI Image] Image -- Canary[Canary Deploy] Canary -- Metrics[Cold Start Metrics]每一关的失败条件要明确阶段阻断条件Build依赖未锁定、SBOM 缺失、测试失败Extractexecutable jar 无法 extracted form 运行Training训练 profile 触发真实写工具、真实模型费用或外部副作用Cacheapp.aot未生成、Java 版本不匹配、cache 过大异常Start-XX:AOTCacheapp.aot启动失败或无效Smoke/actuator/health/readiness不通过、ChatClient 基础路径失败Eval高风险 Agent 样本失败、工具策略失败Canary冷启动和首请求 p95 退化超过阈值训练运行本身也要版本化agent-aot-training: version: 2026-07-01 scenarios: - name: boot-context-refresh sideEffects: none - name: chat-client-build sideEffects: none - name: tool-schema-render sideEffects: none - name: structured-output-conversion sideEffects: none forbidden: - real-model-call - write-tool-call - production-database-write - external-notification这能避免训练脚本慢慢演变成一个没人敢动的隐式流程。11. 什么时候不要急着上 AOT CacheAOT Cache 很值得试但不是所有服务都要马上改。不建议优先投入的场景服务常驻、实例很少重启冷启动不是 SLO 问题。主要延迟完全来自外部模型或第三方工具启动时间占比很小。运行时频繁改变 classpath 或动态加载插件。每个租户都有完全不同的 Bean 图训练与生产路径差异很大。还没有基本启动指标、首请求指标和发布回归集。更适合优先投入的场景Agent API 随流量自动扩缩容冷 pod 经常接请求。内部工具 Agent 部署在 Serverless 或 scale-to-zero 平台。蓝绿/金丝雀发布频繁滚动期间 p99 抖动明显。首次 ChatClient、RAG、Tool Calling 路径明显慢于稳定状态。团队还不想承受 Native Image 的动态性约束。12. 实战落地清单如果团队今天就要开始可以按这个顺序推进记录当前普通 JAR 的ApplicationReadyEvent时间、首请求时间和首 token 时间。升级到 Java 25 测试环境确认 Spring Boot 4.1 应用能用 extracted form 启动。用-XX:AOTCacheOutputapp.aot -Dspring.context.exitonRefresh生成第一版 cache。用-XX:AOTCacheapp.aot启动确认 cache 有效且 readiness 正常。把app.aot、JDK 版本、classpath hash、训练 profile 写入制品元数据。对 Agent 服务补aot-trainingprofile确保训练不触发真实模型和写工具。对首请求路径增加 smokeChatClient 构建、tool schema、structured output、vector client。对比普通 JAR、AOT cache、Spring AOT AOT cache 三组数据。在 canary 阶段观察冷启动 p95、首请求 p95、内存和错误率。只有在收益稳定后再考虑更复杂的 training traffic、CRaC 或 Native Image。13. 今日结论Java 25 的 AOT Cache 和 Spring Boot 4.1 的支持让 Java Agent 服务多了一个很务实的优化方向在不放弃 JVM/JIT 动态能力的前提下把冷启动和暖机前移到训练与构建阶段。对 Spring AI Agent 来说这件事的价值不在于“模型更快”而在于新 pod 更快 ready。首次 Agent 请求更接近稳定状态。扩缩容和发布期间 p99 更可控。训练、启动、制品、指标可以进入 CI/CD 和 SLO。真正落地时要记住三条边界AOT Cache 绑定应用版本、classpath、Java 版本和训练路径不能跨版本乱复用。Spring AOT 可以叠加但要求 Bean 图和运行时配置更稳定。Agent 训练运行必须无副作用不能把真实模型调用、写工具和生产数据带进 cache 生成流程。最终目标不是炫启动日志而是让 Agent 服务回答一个生产问题当流量突然进来、实例刚刚扩容、版本刚刚滚动时我能多快、稳定、可观测地开始服务参考资料Spring Boot AOT CacheAOT Cache :: Spring BootSpring Boot Ahead-of-Time Processing With the JVMAhead-of-Time Processing With the JVM :: Spring BootOpenJDK Project LeydenProject LeydenJEP 514 Ahead-of-Time Command-Line ErgonomicsJEP 514: Ahead-of-Time Command-Line ErgonomicsJEP 515 Ahead-of-Time Method ProfilingJEP 515: Ahead-of-Time Method ProfilingJEP 516 Ahead-of-Time Object Caching with Any GCJEP 516: Ahead-of-Time Object Caching with Any GCOpenJDK JDK 26 BuildsOpenJDK JDK 26.0.1 GA ReleaseSpring AI 项目页Spring AISpring Boot 项目页Spring BootSpring Cloud 项目页Spring Cloud