将 Java 应用程序容器化
本文概述了用于容器化 Java 应用程序的推荐策略和设置。
在容器化 Java 应用程序时,请仔细考虑容器可用的 CPU 时间。 然后,考虑在总内存量和 Java 虚拟机(JVM)的堆大小方面,可用内存量。 在容器化环境中,应用程序可能有权访问所有处理器,因此能够并行运行多个线程。 不过,通常,容器应用的 CPU 配额可能会限制对 CPU 的访问。
JVM 具有启发式方法,用于根据 CPU 配额确定“可用处理器”的数量,这可以极大地影响 Java 应用程序的性能。 分配给容器本身的内存以及 JVM 堆区域的大小与处理器一样重要。 这些因素将确定垃圾回收器(GC)的行为以及系统的整体性能。
容器化新应用程序
为新应用程序容器化 Java 工作负荷时,在考虑内存时,必须考虑到两点:
- 分配给容器本身的内存。
- Java 进程可用的内存量。
了解 JVM 默认的人体工学
应用程序需要起点和设置。 JVM 具有默认的人体工学,其预定义值基于系统中的可用处理器数和内存量。 当 JVM 启动时不使用特定的启动标志或参数时,将使用以下表中所示的默认值。
下表显示了用于可用资源的默认 GC:
可用资源 | 默认 GC |
---|---|
任意数量的处理器 最多 1791 MB 内存 |
SerialGC |
2 个以上的处理器 1792 MB 或更多内存 |
G1GC |
下表显示了默认的最大堆大小,具体取决于 JVM 正在运行的环境中可用的内存量:
可用内存 | 默认最大堆大小 |
---|---|
最多 256 MB | 50% 的可用内存 |
256 MB 到 512 MB | ~127MB |
超过 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 堆大小:
说明 | 标记 | 示例 |
---|---|---|
固定值 | -Xmx |
-Xmx4g |
动态值 | -XX:MaxRAMPercentage |
-XX:MaxRAMPercentage=75 |
最小/初始堆大小
如果保证环境对 JVM 实例保留一定数量的内存(例如在容器中),则应将最小堆大小(或初始堆大小)设置为与最大堆大小相同的大小。 此设置向 JVM 指示它不应执行将内存释放到 OS 的任务。
若要设置最小堆大小,请 -Xms
用于绝对量或 -XX:InitialRAMPercentage
百分比量。
重要
尽管名称建议,但标志 -XX:MinRAMPercentage
用于为系统中最多提供 256 MB RAM 的系统设置默认 的最大 RAM 百分比。
确定要使用的 GC
以前,你确定要开始使用的 JVM 堆内存量。 下一步是选择 GC。 选择 GC 通常是选择 GC 的最大 JVM 堆内存量。 下表描述了每个 GC 的特征。
因素 | SerialGC | ParallelGC | G1GC | ZGC | ShenandoahGC |
---|---|---|---|---|---|
内核数 | 1 | 2 | 2 | 2 | 2 |
多线程 | 否 | 是 | 是 | 是 | 是 |
Java 堆大小 | <4 GB | <4 GB | >4 GB | >4 GB | >4 GB |
暂停 | 是 | 是 | 是 | 是(<1 毫秒) | 是(<10 毫秒) |
开销 | 最小 | 最小 | 适中 | 适中 | 适中 |
Tail-latency Effect | 高 | 高 | 高 | 低 | 中等 |
JDK 版本 | 全部 | 全部 | JDK 8+ | JDK 17+ | JDK 11+ |
最适用于 | 单核小型堆 | 具有任何堆大小的多核小型堆或批处理工作负荷 | 在中型到大型堆中响应(request-response/DB 交互) | 在中型到大型堆中响应(request-response/DB 交互) | 在中型到大型堆中响应(request-response/DB 交互) |
提示
对于大多数常规用途微服务应用程序,请从并行 GC 开始。
确定需要多少个 CPU 核心
对于除 SerialGC 以外的任何 GC,建议在 Kubernetes 上至少cpu_limit两个或更多个 vCPU 核心。2000m
不建议在容器化环境中选择小于 1 个 vCPU 核心的任何内容。
提示
如果不知道要开始使用多少个核心,则一个不错的选择是 2 个 vCPU 核心。
选取起点
建议从容器业务流程环境中(例如 Kubernetes、OpenShift、Azure Spring Apps、Azure 容器应用和Azure App 服务)中的两个副本或实例开始。 下表总结了新 Java 应用程序容器化的建议起点。
vCPU 核心数 | 容器内存 | JVM 堆大小 | GC | 副本 |
---|---|---|---|---|
2 | 4 GB | 75% | ParallelGC | 2 |
要使用的 JVM 参数包括: -XX:+UseParallelGC -XX:MaxRAMPercentage=75
容器化现有(本地)应用程序
如果应用程序已在本地或云中的 VM 上运行,则建议从以下开始:
- 应用程序当前有权访问的内存量相同。
- 应用程序当前提供的 CPU 数(vCPU 核心数)相同。
- 当前使用的 JVM 参数相同。
如果 vCPU 核心和/或容器内存组合不可用,则选取最近的一个,将 vCPU 核心和容器内存舍入。
后续步骤
了解用于容器化 Java 应用程序的一般建议后,请继续阅读以下文章来建立容器化基线: