将 Java 应用程序容器化

本文概述了用于容器化 Java 应用程序的推荐策略和设置。 在容器化 Java 应用程序时,请仔细考虑容器可用的 CPU 时间。 然后,考虑在内存总量和 Java 虚拟机(JVM)的堆大小方面可用的内存量。 在容器化环境中,应用程序可能有权访问所有处理器,因此能够并行运行多个线程。 不过,通常,容器应用了 CPU 配额,这可能会限制对 CPU 的访问。

JVM 具有启发式方法,用于根据 CPU 配额确定“可用处理器”的数量,这可以极大地影响 Java 应用程序的性能。 分配给容器本身的内存以及 JVM 堆区域的大小与处理器一样重要。 这些因素决定了垃圾回收器(GC)的行为以及系统的整体性能。

容器化新应用程序

为新应用程序容器化 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 的系统。

显示 OpenJDK 17 上默认堆大小的图表。

确定要使用的 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

容器化现有的本地应用程序

如果应用程序已在本地或云中的 VM 上运行,则建议从以下配置开始:

  • 应用程序当前可以访问的内存量相同。
  • 与应用程序当前可用的 CPU 或 vCPU 核心数量相同。
  • 您当前使用的相同 JVM 参数。

如果 vCPU 核心或容器内存组合不可用,则选取最近的一个,将 vCPU 核心和容器内存四舍五入。

后续步骤

了解用于容器化 Java 应用程序的一般建议后,请继续阅读以下文章来建立容器化基线: