当前位置: 首页 > news >正文

使用正确的 JVM 功能加速现有部署

一 前言

在当今的技术格局中,Java 应用程序面临着严峻的挑战。企业需要满足日益增长的性能需求,同时控制基础设施成本,并且避免昂贵的重构工作。

随着云计算的普及,这一挑战愈发严峻。如今,每一毫秒的延迟和每一兆字节的内存消耗都会直接影响到日常运营成本。这些数字令人震惊:根据我的经验,配置不当的 JVM 通常会导致云成本增加 30% 到 50%。对于大型企业而言,这意味着数百万美元的可避免开支。

二 常见的优化错误

尽管 JVM 优化的成本影响显而易见,但大多数组织却未能有效实施。我们反复观察到以下四个常见错误:

  • 接受默认设置 :许多团队只是使用开箱即用的 JVM 配置。这些默认设置更注重兼容性而非性能,导致堆利用率低下、垃圾收集模式不理想,以及启动和预热阶段资源消耗大。
  • 复制随机配置 :当团队尝试进行优化时,他们经常使用来自博客或论坛的设置。这些针对不同工作负载或 JVM 版本设计的设置,往往会降低性能。
  • 投入资源解决问题 :许多组织不去解决核心问题,而是简单地添加更多 CPU、内存和机器。这形成了成本不断增长的恶性循环,却没有解决根本问题。
  • 关注错误的优化 :即使是专门的性能工作也常常会关注错误的领域。团队可能会花费数周时间对垃圾收集进行微调,而忽略了影响更大的简单配置更改。

三 了解当前新的趋势

在探索我们可以获得的快速胜利之前,我们需要认识到创造当今 JVM 格局的根本性转变:

  • 容器化 :现在大多数 Java 应用程序都在容器中运行。这为 JVM 如何应对资源限制带来了新的挑战,但也带来了新的优化机会。
  • 高级 JVM 功能 :现代 JVM 包含大多数部署未使用的强大性能功能。从复杂的垃圾收集器到启动加速技术,这些功能只需极少的实施工作即可带来显著优势。
  • 云的成本优势 :随着应用程序迁移到云端,性能与运营成本直接相关。优化不再仅仅是为了用户体验,而是成本控制的必要条件。
    这些变化要求采用一种新方法来提高 JVM 性能,即利用现代功能来解决容器化、云原生部署的特定挑战。
    在本文中,我们将逐步探索更为复杂的优化技术,这些技术可以以最小的努力显著提高性能。
    让我们从基础开始,开启 JVM 性能提升之旅:正确的容器配置。无需更改应用程序,只需智能构建 Dockerfile 并进行 JVM 设置,即可立即获得性能提升。这些快速提升 Java 应用程序性能的方法,是您以最小的努力和风险消除性能债务的第一步。

3.1 通过容器优化轻松实现性能提升

容器革命从根本上改变了我们打包和部署 Java 应用程序的方式。然而,许多组织未能意识到容器配置本身就代表着一个关键的性能优化机会。即使在考虑复杂的 JVM 调优之前,我们构建容器镜像和配置其运行时环境的方式也能带来显著的性能提升。

3.1.1 基础镜像很重要

任何容器化 Java 应用程序的起点都是选择合适的基础镜像。这个看似简单的决定会对从启动时间到内存消耗等各个方面产生连锁影响。

大多数开发者默认使用通用 Linux 发行版作为基础镜像。这些发行版的大小通常在 300MB 左右,包含数百个与 Java 应用程序执行无关的实用程序、库和服务。一些提供商提供其操作系统的轻量级版本,例如 Red Hat 的 UBI Minimal(80-100MB)或 Debian Slim。这已经是向前迈出的一步;然而,它还可以进一步改进。

基于 Musl 的 Linux 发行版比使用 glib 的发行版体积小得多。BellSoft 抓住了这一机遇,于 2019 年向 OpenJDK 项目贡献了 Alpine Linux 端口 (JEP 386),从而创建了合适的 musl libc 支持。这项创新显著缩减了 Java 应用程序的镜像大小。

Alpaquita 是一个专门针对 Java 工作负载进行优化的 Linux 发行版,它进一步优化了基础镜像。它提供了较小的镜像大小,并支持 musl 和 glibc 两种版本,同时还针对 JVM 工作负载进行了定制的运行时优化,我们将在本文中进一步讨论。

或者,选择生产就绪的 Java 镜像,例如 Liberica Runtime Container。它包含 Alpaquita Linux 和 Liberica JDK Lite(OpenJDK 构建的精简版本)。此组合可为云部署节省高达 30% 的 RAM 和磁盘空间,且无需任何手动调整。

另一个简单的优化是在生产容器中使用“JRE”而不是完整的 JDK。这种区别在具有数十或数百个实例的微服务架构中尤为显著,可以在不影响性能的情况下减少 15-25%的内存占用。

这些优化不仅仅是为了减小镜像大小——尽管这本身就能缩短部署时间并减少网络流量。更重要的是,专用 Java 基础镜像通常包含面向性能的配置,并支持通用镜像所缺乏的性能特性。

3.1.2 应用层分离

容器镜像分层虽然看似一个技术实现细节,但却代表了性能优化的另一个机会。

不要将所有内容捆绑到单个层中,而要根据组件的变化频率来分离它们:

# Inefficient approach - single layer for all dependencies
COPY ./target/application.jar app.jar# Optimized approach - separating layers by change frequency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app

这种方法利用 Docker 的缓存机制,仅重建和传输已更改的内容。更新微服务时,您可能只需传输 10KB 的修改代码,而无需传输超过 200MB 的整个镜像。对于数百个容器和频繁部署的情况,这可以将网络流量减少几个数量级。

3.1.3 JVM 内存配置

最有效的容器优化是合理的 JVM 内存配置。默认情况下,较旧的 JVM 无法识别容器内存限制,从而导致内存不足或资源利用不足。

简单的解决方案是显式内存配置:MaxRAMPercentage=80 :使用 80% 的容器内存(根据应用程序类型进行调整)

# Common mistake - no memory settings 
ENTRYPOINT ["java", "-jar", "app.jar"] # Optimal approach - percentage-based settings 
ENTRYPOINT ["java", \ 
"-XX:MaxRAMPercentage=80", \ 
"-jar", "app.jar"]

这些简单的更改正确地将 JVM 资源使用情况与容器边界对齐,通常无需任何代码修改即可将吞吐量提高 20-40%。

这些容器优化代表了提升 Java 应用程序性能的首批速效方案。从基础镜像选择到内存配置,每一步都以最小的努力带来显著的收益,无论团队的专业水平如何,都能轻松掌握这些优化方法。

接下来,我们将解决另一个关键的性能挑战:启动时间优化。通过理解并解决 Java 应用程序资源密集型的初始化阶段,我们可以进一步大幅提升部署效率和资源利用率。

3.2 实现接近零的启动时间

Java 应用程序表现出一种资源消耗模式,这给扩展带来了巨大的挑战。它们并非在整个生命周期内保持一致的资源使用率,而是呈现出一种独特的“峰值-稳定”模式:资源使用率在启动时最初非常高,然后在正常运行时显著下降。

这种资源消耗状况会导致根本性的扩展效率低下。水平扩展会变得棘手,因为每个新的 Java 实例都需要这个密集的初始化阶段。添加更多实例实际上会暂时降低系统整体性能。为什么?因为你的资源被启动进程消耗了,而不是处理实际的请求。

让我们看一下使用典型的 Spring Boot 应用程序进行测试的实际性能数据:

  • CPU 使用率 :启动时峰值为 400%(4 核),稳定状态下稳定在 ~100%
  • 内存使用率 :堆初始化时开始较高,经过几次垃圾收集周期后趋于稳定
  • 响应时间 :首次请求所需的时间比稳定状态响应长 5-10 倍
  • 启动时长 :41 秒,以每秒 1000 个请求的速度完全运行(在测试条件下)
    在这里插入图片描述
    在这里插入图片描述
    在初始化要求更复杂的生产环境中,启动时间可能会延长至 15-20 分钟。在此启动期间,响应时间可能比正常情况慢 5-10 倍,并且错误率通常会随着连接池和缓存的初始化而飙升。这带来了一个关键的扩展问题:您不能简单地将资源翻倍来获得双倍的性能。未经优化的 Java 应用程序不可避免地会导致资源利用率低下和云费用增加,同时在扩展期间仍会提供不一致的用户体验。

3.2.1 减少启动时间的 4 种方法

Java 生态系统提供了几种有效且快速的解决方案来解决启动时间难题。每种方案都代表了一种逐步完善的解决初始化瓶颈的方法:

3.2.1.1 级别 0:客户端虚拟机 – 简单交换

最简单的方法利用经常被忽视的客户端虚拟机,它优先考虑启动性能而不是长期吞吐量优化:

ENTRYPOINT ["java", "-client", "-jar", “app.jar"]

注意:您需要一个实际包含客户端 VM 的 JDK 包。

使用标准 Spring Boot 应用程序进行测试显示:

  • 启动时间 :减少 15-25%
  • CPU 消耗 :初始峰值较低,初始化时通常需要 1-2 个核心,而不是 3-4 个
  • 内存占用 :大约小 10MB
  • 评估 :持续高负载期间峰值吞吐量可能会降低
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    这种优化对于无服务器部署、短期进程以及吞吐量要求适中的应用程序尤其有价值。例如,AWS Lambda 建议设置 -XX:TieredStopAtLevel=1,这使得服务器虚拟机执行类似于客户端虚拟机的编译,从而显著减少冷启动时间,同时保持无服务器函数的合理性能。
3.2.1.2 级别 1:类数据共享 (CDS) – 重用已加载的类

类数据共享 (CDS) 解决了 Java 启动过程中最耗资源的环节之一:类加载。通过创建已加载类的内存映射存档,CDS 显著减少了启动时间和内存使用量:

#First Stage: create a shared archive
java -XX:ArchiveClassesAtExit=./application.jsa -jar target/app.jar#Start the application and use the shared archive: 
java -XX:SharedArchiveFile=application.jsa -jar target/app.jar

对于 Spring Boot 应用程序,通过内置支持,这种优化可以更加简单:

#Create an executable JAR
FROM bellsoft/liberica-runtime-container:jdk-23-stream-musl as builderWORKDIR /home
ADD app /home/app
RUN cd app && ./mvnw clean package#Create a layered JAR
FROM bellsoft/liberica-runtime-container:jdk-23-cds-slim-musl as optimizerWORKDIR /app
COPY --from=builder /home/app/target/*.jar app.jar
RUN java -Djarmode=tools -jar app.jar extract --layers --launcher#Copy application layers to the fresh base image and create the archive 
FROM bellsoft/liberica-runtime-container:jdk-23-cds-slim-muslCOPY --from=optimizer /app/app/dependencies/ ./
COPY --from=optimizer /app/app/spring-boot-loader/ ./
COPY --from=optimizer /app/app/snapshot-dependencies/ ./
COPY --from=optimizer /app/app/application/ ./#Run the application in container to create the archive. The app will exit automatically. Enable Spring AOT.
RUN java -Dspring.aot.enabled=true \-XX:ArchiveClassesAtExit=./application.jsa \-Dspring.context.exit=onRefresh \org.springframework.boot.loader.launch.JarLauncher#Run the application with Spring AOT enabled and using the shared archive
ENTRYPOINT ["java", \"-Dspring.aot.enabled=true", \"-XX:SharedArchiveFile=application.jsa", \"org.springframework.boot.loader.launch.JarLauncher"]

测试表明,CDS 通常能够实现以下功能:

  • 启动时间 :减少 30-40%
  • 节省内存 :由于共享元空间,内存使用量降低 5-15%
  • CPU 减少 :初始 CPU 峰值不那么明显
  • 兼容性 :几乎适用于所有 Java 应用程序,无需更改代码

在这里插入图片描述
在这里插入图片描述

3.2.1.3 级别 2:原生镜像 – 从一开始就编译

GraalVM Native Image 采取了更为激进的方法,提前将 Java 应用程序编译为独立的本机可执行文件:

# Build stage using GraalVM CE-based Liberica Native Image Kit
FROM bellsoft/liberica-native-image-kit-container:jdk-21-nik-23.1-stream-musl
WORKDIR /home/myapp
COPY Demo.java /home/myapp/
RUN javac Demo.java
RUN native-image Demo# Run stage
FROM bellsoft/alpaquita-linux-base:stream-musl
WORKDIR /home/myapp
COPY --from=0 /home/myapp/demo.
CMD [“./demo”]

对于 Spring Boot 应用程序,通过内置支持简化了原生镜像流程:./mvnw -Pnative spring-boot:build-image。
Native Image 显著提升了启动性能:

  • 启动时间 :快 10-100 倍(以毫秒为单位,而不是秒)
  • 内存占用 :通常小 50%
  • 评估 :一些反射限制,可能降低峰值吞吐量
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
3.2.1.4 级别 3:检查点/恢复 (CRaC) – 冻结正在运行的应用程序

最先进的方法是使用 OpenJDK 项目“检查点协调还原 (CRaC)”。CRaC 的工作原理类似于对已完全初始化并正在运行的 Java 应用程序进行快照,然后将其“冻结”。之后,您可以立即“解冻”并恢复此快照,从而绕过整个启动和预热阶段:

#Build the container image, start and exit the application in a container to create a file with a checkpointed application
FROM bellsoft/liberica-runtime-container:jdk-21-crac-musl as builder
WORKDIR /home
ADD app /home/app
RUN cd app && ./mvnw clean packageFROM bellsoft/liberica-runtime-container:jre-21-crac-slim-musl as optimizerWORKDIR /app
COPY --from=builder /home/app/target/app.jar /app/app.jarRUN java -Djarmode=tools -jar app.jar extract --layers --launcherFROM bellsoft/liberica-runtime-container:jre-21-crac-slim-musl# We stay root in a container to use CRaC
VOLUME /tmp
EXPOSE 8080COPY --from=optimizer /app/app/dependencies/ ./
COPY --from=optimizer /app/app/spring-boot-loader/ ./
COPY --from=optimizer /app/app/snapshot-dependencies/ ./
COPY --from=optimizer /app/app/application/ ./ENTRYPOINT ["java", "-Dspring.context.checkpoint=onRefresh", "-XX:CRaCCheckpointTo=/checkpoint", "-XX:MaxRAMPercentage=80.0", "org.springframework.boot.loader.launch.JarLauncher"]

以下是在部署管道中实现 CRaC 的完整工作流程 - 从容器初始化到创建检查点,最后从保存状态启动应用程序:

#Build the container image
docker build . -t app-crac-checkpoint -f Dockerfile-crac#Start the container image
docker run --cap-add CHECKPOINT_RESTORE --cap-add SYS_PTRACE --name app-crac app-crac-checkpoint#Transfer the contents of the image into a new image and change the entry point to restart the application from the file:
docker commit --change='ENTRYPOINT ["java", "-XX:CRaCRestoreFrom=/checkpoint"]' app-crac app-crac-restore#Remove the first container:
docker rm -f app-crac#Run the second image with the checkpointed application:
docker run -it --rm -p 8080:8080 --cap-add CHECKPOINT_RESTORE --cap-add SYS_PTRACE --name app-crac app-crac-restore

CRaC 提供终极的启动优化:

  • 启动时间 :接近瞬时(毫秒)
  • 内存效率 :预初始化堆,无早期垃圾收集动荡
  • CPU 节省 :消除几乎所有初始化 CPU 峰值
  • 使用条件 :需要与 CRaC 兼容的 JVM(例如,Liberica JDK CRaC)和应用程序更改以实现安全和干净的检查点/恢复
    在这里插入图片描述
    在这里插入图片描述
3.2.1.5 未来:Leyden项目

Leyden 项目代表了我们讨论过的优化技术的自然演进。它并非遥不可及的未来技术,而是正在积极开发中,旨在超越 OpenJDK 中 AppCDS 的功能。Leyden 基于 AppCDS,旨在创建一个统一的缓存,不仅保存已加载的类,还保存已编译的代码、方法配置文件和链接的类。Leyden 将适用于所有受支持的 Java 平台,并在初始训练运行后提供无状态操作。这种全面的方法有望将 AOT 编译的启动优势与完全预热的 JVM 的运行时性能相结合,同时不受现有解决方案的限制。

3.2.2 选择正确的启动方法

每种启动优化方法都有各自的优缺点。对于大多数应用程序来说,渐进式实施方法效果最佳:

  • 从客户端 VM 和/或 CDS 开始,以最小的风险获得直接利益
  • 评估 Native Image 或 CRaC 是否适用于具有关键启动要求的应用程序

通过选择正确的启动优化方法,组织可以显著提高资源利用率,实现更高效的扩展并显著降低云成本。

3.3 垃圾收集:选择正确的收集器是关键

垃圾回收 (GC) 是 Java 应用程序中自动回收未使用内存的过程。虽然它消除了手动内存管理的需要,但不当的 GC 配置可能会导致频繁或长时间的暂停,从而影响应用程序的响应能力和吞吐量。

垃圾收集一直被认为是 JVM 调优中最复杂的环节。许多团队花费数周时间对 GC 参数进行微调,但效果往往微乎其微。然而,现代 JVM 提供了一种更简单、更有效的方法:根据应用程序的具体需求选择合适的垃圾收集器。

应用程序类型收集器开关优化内容
延迟敏感的 APIG1 → ZGC最大暂停时间减少 90-99%
批处理G1 → Parallel吞吐量提高 10-15%
内存受限G1 → Serial减少 5-10% 的内存占用

仅仅选择合适的收集器就能带来显著的性能提升。更令人印象深刻的是,这些提升只需一次配置更改即可实现,这与传统的 GC 调优不同,后者可能涉及数十个参数。因此,与其试图掌握数十个复杂的调优参数,GC 优化最有效且快速的方法是根据您的性能目标选择合适的收集器。

  • 适用于低延迟应用程序:ZGC 和 Shenandoah。 无论堆大小如何(即使 100GB 以上),它们都能提供亚毫秒级的暂停时间,并且在内存压力下也能保持可预测的性能,尽管吞吐量会略有下降。
  • 最大吞吐量:并行 GC。 它为批处理和 CPU 密集型应用程序提供了 10-15% 的更高吞吐量,但代价是暂停时间更长。
  • 平衡关注点:G1 GC。G1 为通用应用程序提供了良好的平衡,具有可配置的暂停目标和合理的吞吐量。
  • 适用于受限环境:串行 GC。 它减少了小型容器中的内存开销和 CPU 争用,使其在资源受限的环境中更加高效。
  • 下一代:分代 ZGC 和 Shenandoah。 这些较新的实现保留了超低的暂停时间,同时将内存使用量减少了 10-25%,并更好地处理了短期对象,从而有效地消除了传统的权衡。

3.4 未来:Lilliput项目

除了收集器选择之外,另一个新兴的快速提高内存效率的方法是 Lilliput 项目,它可以减少 Java 对象头的大小:

# Enable Lilliput for reduced object header size (JDK 24+ experimental)ENTRYPOINT ["java", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCompactObjectHeaders", "-jar", "app.jar"]

Lilliput 无需任何代码修改,即可将对象密集型应用程序中的堆内存需求减少 10-20%。该项目旨在解决 Java 长期以来存在的内存效率低下问题之一,即对象头占用了过多的堆空间。

3.5 GC 调优实践策略

现代 GC 优化方法涉及从简单到高级的技术进展:

  • 首先根据您的主要性能目标选择收集器
  • 添加适合您的容器环境的内存大小
  • 考虑像 Lilliput 这样的实验性功能进行高级优化
  • 只有在必要时才考虑详细的参数调整

这种方法比传统的 GC 调优方法大大简化,投资回报率也更高。需要成为 GC 专家才能实现卓越 Java 性能的日子已经一去不复返了。

3.6 历史应用程序:一切希望尚未破灭

到目前为止,我们讨论的优化技术在现代 JVM(主要是 Java 17 及更高版本)上充分发挥了其优势。然而,企业 Java 部署的现实带来了一个重大挑战:大多数生产工作负载仍然运行在旧版本上。

根据各种来源(New Relic 的 2024 年 Java 生态系统状况或 JetBrains 的 2023 年开发生态系统报告),29-50% 的受访者继续使用 Java 8,而 32-38% 的受访者依赖 Java 11。在 BellSoft 的 Java 开发者调查中,三分之二的受访者承认仍在 Java 11 或更早版本上运行应用程序。

在这里插入图片描述
这些统计数据反映了一个常见的企业现实:将旧版应用程序迁移到较新的 Java 版本通常会带来重大的技术和业务挑战。其结果是性能和效率方面的差距,运行旧版 Java 的组织会错过较新 JDK 中提供的关键优化。

3.7 融合 JDK:为传统应用程序提供现代性能

幸运的是,对于遗留 Java 应用程序来说,有一个强大的快速解决方案:融合 JDK。这些专用发行版将旧版 Java 的 API 兼容性与现代或自定义 JVM 实现的性能改进相结合。

Liberica JDK 性能版是这种方法的一个完全开源的示例。它在 Java API(应用程序代码与之交互的 API)和 JVM 实现(执行代码的运行时引擎)之间保持了清晰的分离:

  • API 层 :与目标 Java 版本(8 或 11)保持 100% 兼容
  • JVM 层 :融入了新版 JVM 的优化

此架构允许应用程序受益于现代 JVM 优化,而无需任何代码更改或兼容性风险。您的应用程序可以继续使用其设计所针对的 Java 8 或 11 API,同时底层 JVM 可利用现代性能改进:

  • 现代垃圾收集 :旧版应用程序可以访问 ZGC 和其他低延迟收集器,无需更改应用程序即可将暂停时间减少高达 90%。
  • 运行时优化 :Java 8 和 11 应用程序受益于较新的 JIT 编译器改进、优化的字符串处理和增强的内部函数,为许多工作负载提供 10-30% 的更好吞吐量。
  • 内存效率 :较新的 JVM 的内存优化减少了占用空间并提高了遗留应用程序的垃圾收集效率。
  • 容器感知 :遗留应用程序获得了原始 Java 8 或 11 版本中没有的容器感知资源管理。

使用标准 Java 基准进行的性能测试显示出了切实的好处:

  • Java 8 上的 Spring Boot 2.7.x 应用程序:吞吐量提高 15-20%
  • Java 8 Web 应用程序:GC 暂停时间减少高达 70%
  • Java 11 批处理:执行时间加快 10-15%

在这里插入图片描述
更重要的是,这些改进不需要更改应用程序代码、不需要复杂的迁移项目以及最少的操作更改 - 只需切换到 Fused JDK 发行版即可带来立竿见影的好处。

在考虑 Fused JDK 的供应商时,务必在您自己的应用程序上测试该发行版。不同的版本可以为不同的工作负载带来不同的性能优势。另一个需要考虑的重要问题是供应商锁定。许多 Fused JDK 都包含自定义 JVM。这不仅会在切换到指定的 JDK 时造成障碍,还会在您决定升级到较新版本的 JDK 时造成障碍。请记住这一点——我们建议您坚持使用完全开源的解决方案,因为它可以让您在未来灵活地调整堆栈。

总而言之,通过利用 Fused JDK,组织可以延长旧版 Java 应用程序的可行寿命,同时显著提高其性能和资源效率——对于继续在旧版 JDK 上运行的大量企业 Java 来说,这确实是一种“合法手段”。

3.8 Buildpacks:Dockerfiles 的终极替代品

虽然手动优化 Dockerfile 和 JVM 设置可以带来显著的优势,但 Buildpacks 的优势更加显著,而且更省力。这项技术完全无需编写 Dockerfile,而是将应用程序源代码直接转换为优化的容器镜像。

Buildpacks 简化了容器化应用的构建流程,同时整合了复杂的 JVM 自定义和优化功能。它们为 AppCDS 和原生镜像等高级功能提供开箱即用的支持,而这些功能原本需要大量的专业知识才能实现。

您无需编写和维护复杂的 Dockerfile,而是可以使用项目的构建系统 Maven 或 Gradle,并使用单个命令构建容器映像:

一个命令替换整个 Dockerfile mvn spring-boot:build-image 或者gradle bootBuildImage 这个简单的命令触发了一个复杂的工作流程:

  • 检测阶段 :buildpack 分析您的代码库以识别应用程序类型。它确定运行该应用程序的要求和规定。
  • 构建阶段 :buildpack 通过创建优化的容器镜像来履行合同。

3.9 零配置性能优势

在大多数情况下,构建包无需任何配置即可运行。默认情况下,它们使用最新版本的优化基础镜像,从而自动提升性能。对于 Spring Boot 应用程序,Paketo 构建包会自动创建具有优化结构的分层 JAR 文件,以加快启动速度并减少内存消耗。我们在本文前面讨论过这一点。

Buildpacks 提供了几个快速修复,您可以轻松利用它们来立即提高性能:

  • 自动化资源计算 :buildpacks 的最大优势之一是能够根据实际应用需求正确计算资源。
  • 快速修补操作系统级漏洞 :Buildpacks 与现代 CI/CD 系统无缝集成。无需对构建工具进行高级定制,即可快速重建您的应用。借助 CI 中的 Buildpacks,您无需重建源代码即可修补应用镜像的操作系统层。
  • 标准化优化方法 :Buildpack 可在整个应用程序组合中强制执行一致的优化。所有应用程序均获得相同级别的优化,而开发人员只需专注于代码,无需担心容器配置。随着新的 JVM 优化方案的出现,Buildpack 会自动将其纳入。

3.10 高级性能调优变得简单

当您需要将性能提升到一个新的高度时,buildpack 的真正威力就显现出来了。您无需学习数十个 JVM 优化参数,只需通过简单的配置即可充分利用 buildpack 的功能。以下是一些示例:

3.10.1 优化快速启动

# Enable AppCDS for faster startups
BP_JVM_CDS_ENABLED=true

3.10.2 切换到原生镜像以实现近乎零的启动

# For Spring Boot 3.x applications./mvnw -Pnative spring-boot:build-image

虽然 Dockerfile 仍然是最常见的容器化方法,但 Buildpack 代表了一种更复杂、更能感知 Java 的解决方案。它凝聚了我们多年的 JVM 优化经验,是一款易于使用的工具,能够持续产生比手动创建容器更好的效果。

对于大多数 Java 应用程序来说,从 Dockerfiles 到 buildpacks 的转变可以以较少的努力带来立竿见影的性能优势,这或许是当今团队可以获得的最简单但最有效的快速胜利。

3.11 建立可持续的优化策略

本文探讨了无需修改代码即可快速提升 Java 应用程序性能的高效方法。这些优化措施形成了一个良性循环:性能提升带来资源节省,从而为进一步的优化工作和最终的现代化升级提供资金。

最佳优化方法取决于您当前的 Java 版本

JDK 版本第一层第二层第三层迁移路径
JDK 8Fused JDK(Liberica JDK 性能版)或 Liberica JDK Lite容器优化使用 Java 11 进行测试
JDK 11Fused JDK(Liberica JDK 性能版)或 Liberica JDK Lite容器优化AppCDS使用 Java 17 进行测试
JDK 17+容器优化,分代 ZGC/ShenandoahCRaC、GraalVM 或 AppCDSLilliput 项目、Buildpacks、虚拟线程(JDK 21+)关注最新动态

3.12 优化与替代方案

当面临性能挑战时,组织通常会考虑三种方法:

  • 只需添加资源 :快速,但会造成不可持续的成本上升并掩盖真正的问题。
  • 完全重写 :解决技术债务,但涉及高成本、风险和时间投入。
  • 战略优化 :以较低的成本和风险提供直接的利益;为计划中的现代化创造喘息空间。

3.13 可持续发展之路

最有效和可持续的 Java 优化策略遵循以下原则:

  • 尽可能保持最新 :较新的 JDK 版本默认包含性能改进
  • 扩展前优化 :在添加更多资源之前解决效率问题
  • 自动优化 :使用构建包实现性能专业知识的民主化
  • 衡量一切 :基于数据而非假设做出决策
  • 渐进式改进 :将优化视为一个持续的旅程

通过应用这些快速见效的方法,即使是老旧的 Java 应用程序也能实现显著的性能提升,而无需承担重写的风险和成本。这种可持续的方法能够平衡当前的性能需求和长期的架构健康,通过演进而非彻底变革来转型您的应用程序。

相关文章:

  • Kaamel视角下的MCP安全最佳实践
  • python-69-基于graphviz可视化软件生成流程图
  • 文件操作、流对象示例
  • 用 Python 实现基于 Open CASCADE 的 CAD 绘图工具
  • 碰一碰发视频源码文案功能,支持OEM
  • VulnHub-DC-2靶机渗透教程
  • 编译型语言、解释型语言与混合型语言:原理、区别与应用场景详解
  • 【C++】STL之deque
  • flutter 中各种日志
  • 无感字符编码原址转换术——系统内存(Mermaid文本图表版/DeepSeek)
  • express查看文件上传报文,处理文件上传,以及formidable包的使用
  • 深入浅出 Python 协程:从异步基础到开发测试工具的实践指南
  • 了解低功耗蓝牙中的安全密钥
  • JavaScript性能优化实战(4):异步编程与主线程优化
  • 从被动运维到智能预警:某省人防办借力智和信通运维方案实现效能跃升
  • NXP----SVR5510芯片layout设计总结
  • 2025年04月24日Github流行趋势
  • 离线电脑安装python包
  • C++智能指针上
  • 深入探索Spark-Streaming:从Kafka数据源创建DStream
  • 《我的后半生》:人生下半场,也有活力重启的可能
  • 南宁市委常委、组织部部长陈川已任广西医科大学党委书记
  • 中国田协通报苏州马拉松“方便门”处理情况:涉事10人禁赛3年
  • 云南洱源县4.8级地震:房屋受损442户,无人员伤亡报告
  • 宁德时代校友红利!副董事长给母校复旦豪捐10亿,曾毓群给交大捐近14亿
  • 国家卫健委:坚决反对美国白宫网站翻炒新冠病毒“实验室泄漏”