本文概述了用于容器化 Java 应用程序的推荐策略和设置。 容器化Java应用程序时,请仔细考虑容器可用的 CPU 时间。 然后,考虑在内存总量和Java虚拟机(JVM)的堆大小方面可用的内存量。 在容器化环境中,应用程序可能有权访问所有处理器,因此能够并行运行多个线程。 不过,通常,容器应用了 CPU 配额,这可能会限制对 CPU 的访问。
JVM 使用启发式方法根据 CPU 配额确定“可用处理器”的数量,这可能会显著影响Java应用程序的性能。 分配给容器本身的内存以及 JVM 堆区域的大小与处理器一样重要。 这些因素决定了垃圾回收器(GC)的行为以及系统的整体性能。
小窍门
如果不想手动优化这些设置,Azure Java() jaz会自动为你应用云原生 JVM 默认值。 它是一个可直接替换 java 命令的替代项,能够检测容器限制并选择最合适的堆大小和 GC 标志。 有关详细信息,请参阅使用 Java Azure 命令启动器自动优化 JVM。
容器化新应用程序
为新应用程序容器化Java工作负荷时,在考虑内存时,请考虑以下两点:
- 分配给容器本身的内存。
- Java 进程可用的内存量。
了解 JVM 默认的人体工学
应用程序需要起点和设置。 JVM 具有默认的自动优化设置,其预定义值基于可用处理器数量和系统内存大小。 JVM 使用下表中所示的默认值,当你在没有特定的启动标志或参数的情况下启动它。
下表显示了可用资源的默认 GC:
| 可用资源 | 默认 GC |
|---|---|
| 任意数量的处理器 最多 1,791 MB 内存 |
SerialGC |
| 2 个以上的处理器 1,792 MB 或更多内存 |
G1GC |
下表显示了默认的最大堆大小,具体取决于 JVM 正在运行的环境中可用的内存量:
| 可用内存 | 默认最大堆大小 |
|---|---|
| 最多 256 MB | 50% 可用内存 |
| 256 MB 到 512 MB | ~127 MB |
| 超过 512 MB | 25% 可用内存 |
默认的初始堆大小为 1/64 的可用内存。 这些值对 OpenJDK 11 及更高版本有效,并适用于大多数发行版,包括 Microsoft Build of OpenJDK、Azul Zulu、Eclipse Temurin、Oracle OpenJDK 等。
确定容器内存
根据应用程序的需求及其独特的使用模式,选择最适合工作负荷的容器内存量。 例如,如果应用程序创建大型对象图,则可能需要比具有许多小型对象图的应用程序所需的内存更多。
小窍门
如果不知道要分配多少内存,则良好的起点为 4 GB。
确定 JVM 堆内存
分配 JVM 堆内存时,请记住 JVM 需要比为 JVM 堆分配的内存量多。 不要将最大 JVM 堆内存设置为等于容器内存量。 此设置可能会导致容器内存不足(OOM)错误和容器崩溃。
小窍门
为 JVM 堆分配 75% 的容器内存。
在 OpenJDK 11 及更高版本上,通过以下方式之一设置 JVM 堆大小:
| DESCRIPTION | 旗帜 | 例子 |
|---|---|---|
| 固定值 | -Xmx |
-Xmx4g |
| 动态值 | -XX:MaxRAMPercentage |
-XX:MaxRAMPercentage=75 |
最小或初始堆大小
如果环境保证为 JVM 实例保留的一定内存量(例如在容器中),请将最小堆大小或初始堆大小设置为与最大堆大小相同的大小。 此设置向 JVM 指示它不应执行将内存释放到 OS 的任务。
若要设置最小堆大小,请 -Xms 用于绝对量或 -XX:InitialRAMPercentage 百分比量。
重要
尽管该名称看起来如此,标志 -XX:MinRAMPercentage 会为系统中可用 RAM 不超过 256 MB 的系统设置默认最大 RAM 百分比。
确定要使用的 GC
以前,你确定要开始使用的 JVM 堆内存量。 下一步是选择 GC。 你拥有的最大 JVM 堆内存量通常会影响你对 GC 的选择。 下表描述了每个 GC 的特征。
| 因素 | SerialGC | ParallelGC | G1GC | ZGC | ShenandoahGC |
|---|---|---|---|---|---|
| 内核数量 | 1 | 2 | 2 | 2 | 2 |
| 多线程 | 否 | 是的 | 是的 | 是的 | 是的 |
| Java 堆大小 | <4 GB | <4 GB | >4 GB | >4 GB | >4 GB |
| 暂停 | 是的 | 是的 | 是的 | 是(<1 毫秒) | 是(<10 毫秒) |
| 开销 | 最少 | 最少 | 中等 | 中等 | 中等 |
| 尾部延迟效应 | 高 | 高 | 高 | 低 | 中等 |
| JDK 版本 | 全部 | 全部 | JDK 8+ | JDK 17+ | JDK 11+ |
| 最适用于 | 单核小堆 | 多核小堆或具有任意堆大小的批处理工作负载 | 在中型到大型堆中响应(请求-响应/数据库交互) | 在中型到大型堆中响应(请求-响应/数据库交互) | 在中型到大型堆中响应(请求-响应/数据库交互) |
小窍门
对于大多数常规用途微服务应用程序,请从并行 GC 开始。
确定所需的 CPU 核心数
对于除 SerialGC 之外的任何 GC,请使用两个或更多 vCPU 核心;或者对于 Kubernetes 上的 2000m,至少使用 cpu_limit。 不要在容器化环境中选择少于一个 vCPU 核心。
小窍门
如果不知道要开始使用多少个核心,一个不错的选择是两个 vCPU 核心。
选择一个起点
从容器业务流程环境中(例如 Kubernetes、OpenShift、Azure Spring Apps、Azure 容器应用 和 Azure 应用服务)中的两个副本或实例开始。 下表总结了新 Java 应用程序容器化的建议起点。
| vCPU 核心数 | 容器内存 | JVM 堆大小 | GC | 仿制品 |
|---|---|---|---|---|
| 2 | 4 GB | 75% | ParallelGC | 2 |
使用以下 JVM 参数:
-XX:+UseParallelGC -XX:MaxRAMPercentage=75
使用用于Java的 Azure 命令启动器自动优化 JVM
前面的部分介绍如何根据容器内存、CPU 核心和工作负荷类型手动选择 JVM 标志。 如果不想自行维护这些设置,Java() 的 jaz会自动应用云原生 JVM 默认值。
适用于Java的 Azure 命令启动器是一个轻型实用工具,位于启动命令和 JVM 之间。 它会检测云端环境,包括容器内存和 CPU 限制,然后为堆大小设置、GC 选择和诊断选择最合适的调优标志。 该方法可减少配置开销,并默认提高资源利用率。
若要使用该工具,请将 java 命令替换为启动脚本或 Dockerfile 中的命令 jaz 。 例如,无需手动调优 JVM 选项:
java -XX:+UseParallelGC -XX:MaxRAMPercentage=75 -jar myapp.jar
使用 jaz:
jaz -jar myapp.jar
该工具包含在Microsoft Build of OpenJDK的容器映像中,因此无需进行额外的设置。 以下 Dockerfile 使用 jaz 从 jar 文件运行 Java 应用程序:
# Use any Microsoft Build of OpenJDK base image
FROM mcr.microsoft.com/openjdk/jdk:25-ubuntu
# Add your application.jar
COPY application.jar /application.jar
# Use jaz to launch your Java application
CMD ["jaz", "-jar", "application.jar"]
有关安装选项、支持的环境和配置详细信息,请参阅Azure命令启动器Java。
容器化现有的本地应用程序
如果应用程序已在本地或云中的 VM 上运行,请从以下配置开始:
- 应用程序当前可以访问的内存量相同。
- 与应用程序当前可用的 CPU 或 vCPU 核心数量相同。
- 您当前使用的相同 JVM 参数。
如果 vCPU 核心数和容器内存的组合不可用,请选择最接近的组合,并将 vCPU 核心数和容器内存向上取整。
后续步骤
了解有关容器化Java应用程序的一般建议后,请继续阅读以下文章来建立容器化基线并简化 JVM 优化: