引言#

在 2026 年的云原生环境中,Java 应用部署到 Kubernetes(K8s)已经比较常见。不过,很多团队仍然沿用早期 JVM 配置方式,这类配置在容器里不一定合适,也容易带来资源浪费或 OOMKilled。

Akamas 在 2026 年 2 月发布的一份调查1提到,60% 的 JVM 工作负载未明确配置 GC不少部署没有显式设置堆内存上限。我把它当作一个值得参考的行业观察,而不是绝对结论。

Java 25 于 2025 年 9 月发布,是继 Java 21 之后的最新长期支持版本(LTS)。而 Java 26(非 LTS)也已于 2026 年 3 月正式发布。本文结合 Java 25/26 的一些新特性,以及 2026 年实验验证数据,整理我当前比较认可的一组 Kubernetes 容器化 JVM 参数思路。

核心前提:容器感知已是本能#

Java 版本与容器感知#

Java 版本 -XX:+UseContainerSupport 默认行为
Java 8u191 之前 ❌ 不支持容器感知
Java 8u191+ ✅ 支持,需手动启用
Java 10+ 默认启用,无需配置
Java 17/21/23/25/26 ✅ 默认启用,高度优化

我的经验是: 使用 Java 10+ 时,通常不需要再手动配置 -XX:+UseContainerSupport。JVM 已经能够读取 Cgroups v2 提供的内存限制和 CPU 配额。

注意: 生产环境通常会优先考虑 Java 21 (LTS) 或 Java 25 (LTS)。刚发布的 Java 26 包含针对 G1 的进一步优化,但作为非 LTS 版本,我会先在非核心业务中试用。

环境变量:利用 JDK_JAVA_OPTIONS 实现配置分离#

在 2026 年的容器化实践里,JDK_JAVA_OPTIONS 是我更倾向使用的应用启动环境变量(Java 9+)。

JDK_JAVA_OPTIONS vs JAVA_TOOL_OPTIONS#

特性 JAVA_TOOL_OPTIONS JDK_JAVA_OPTIONS
引入版本 Java 5+ Java 9+
作用范围 所有 JDK 工具(java, javac 等) java 命令
优先级 较低 较高(覆盖 JAVA_TOOL_OPTIONS)
适用场景 全局默认、镜像层配置 应用部署、运行时覆盖

一组可讨论的配置示例#

env:
- name: JDK_JAVA_OPTIONS
  value: >-
    -XX:MaxRAMPercentage=75.0
    -XX:InitialRAMPercentage=75.0
    -XX:MinRAMPercentage=75.0
    -XX:+AlwaysPreTouch
    -XX:+UseG1GC
    -Xlog:gc*:file=/logs/gc.log:time,uptime:filecount=5,filesize=10M    

内存管理:百分比胜过固定值#

在 K8s 中,我通常不建议继续写死 -Xmx 固定值,而是优先用百分比控制,以适应 Pod 的资源变化。

2026 年的新标准:MaxRAMPercentage=75.0#

过去(Java 8/11)常见建议是 70%,但在 Java 25 这组假设下,75% 在我参考的数据和实验里是一个可以讨论的平衡点:

  • 虚拟线程 (Virtual Threads):Java 21/25 大规模普及虚拟线程,线程栈(Stack)开销从传统的 1MB/线程 大幅降低,非堆压力减小。
  • G1 记忆集优化 (JDK-8343782):Java 25 对 G1 内部数据结构进行了压缩,显著降低了 GC 自身的堆外内存占用。
  • 对象头压缩 (JEP 519):Java 25 引入了紧凑对象头相关能力;如果运行时配置允许,堆内对象会更紧凑,进而改善一部分内存利用率。
参数 这次整理里更倾向的值 说明
-XX:MaxRAMPercentage 75.0 预留 25% 给非堆空间(Metaspace、Direct Memory、OS)
-XX:InitialRAMPercentage 75.0 Guaranteed QoS:避免启动期频繁扩容导致的性能抖动
-XX:MinRAMPercentage 75.0 确保小容器行为一致
-XX:+AlwaysPreTouch 开启 启动时预申请物理页,消除 Page Fault 带来的长尾延迟

注意: 如果应用使用了大量 Netty 直接内存RocksDB 本地存储,请将 MaxRAMPercentage 下调至 60.0-65.0

GC 选择:明确意图,拒绝 Ergonomics 陷阱#

根据上面的调查,不少工作负载因为没有显式配置 GC 而遇到了意料之外的性能问题

为什么我倾向显式指定 -XX:+UseG1GC#

虽然 Java 21+ 在 Server 模式下默认 G1,但在 K8s 环境中:

  1. 微容器降级:当 CPU < 2 核或内存 < 1792MB 时,JVM 会静默降级为 Serial GC
  2. JEP 523 进展:虽然 Java 26 已提议让 G1 成为全环境默认,但在这一过程完全落地前,显式指定仍然是相对更稳妥的跨版本做法。

GC 选择指南 (2026)#

应用场景 更值得优先评估的 GC 配置参数
很多常见应用 G1GC -XX:+UseG1GC
超低延迟 (<10ms) Generational ZGC -XX:+UseZGC -XX:+ZGenerational
大规模批处理 Parallel GC -XX:+UseParallelGC

Generational ZGC 进阶配置(Java 21+)#

对于需要亚毫秒级停顿的服务(如实时交易、API 网关):

- name: JDK_JAVA_OPTIONS
  value: "-XX:+UseZGC -XX:+ZGenerational -XX:MaxRAMPercentage=70.0"

注:ZGC 需要预留略多于 G1 的非堆空间,建议堆占比设为 70.0% 或更低。

CPU 资源:远离 500m 陷阱#

至少分配 1 核 CPU#

从我看过的实验结果里,CPU 限制 (Limits) 是导致 Java 应用性能明显下滑的一个常见原因

  • CPU Throttling:500m 限制意味着 JVM 线程在 CFS 周期内会被频繁挂起;按我参考的实验现象,GC 停顿可能从几十毫秒升到几百毫秒。
  • ActiveProcessorCount:Java 25 虽然能更好地识别时间片,但物理算力的缺失无法通过算法补偿。

建议: 如果业务确实比较吃 CPU,生产环境可以先从 Request ≥ 1.0 CPU、Limit 为 Request 的 1.5-2 倍开始验证。

完整的 K8s Deployment 部署模板#

apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-app-2026
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: app
        image: my-java-app:v25
        resources:
          requests:
            memory: "1.5Gi"
            cpu: "1"
          limits:
            memory: "2Gi"
            cpu: "2"
        env:
        - name: JDK_JAVA_OPTIONS
          value: >-
            -XX:MaxRAMPercentage=75.0
            -XX:InitialRAMPercentage=75.0
            -XX:+AlwaysPreTouch
            -XX:+UseG1GC
            -Xlog:gc*:file=/logs/gc.log:time,uptime:filecount=5,filesize=10M
            -XX:+HeapDumpOnOutOfMemoryError
            -XX:+ExitOnOutOfMemoryError            
        volumeMounts:
        - name: logs
          mountPath: /logs
      volumes:
      - name: logs
        emptyDir: {}

总结#

如果只保留这次整理里我比较在意的几点,大概是:

  1. 版本:如果团队准备升级,我会优先看 Java 25 LTS。
  2. 变量:在 Java 9+ 的容器环境里,我更倾向先用 JDK_JAVA_OPTIONS
  3. 内存75.0% 是这轮资料和实验里值得优先验证的起点,不一定适合所有负载。
  4. GC:显式指定 -XX:+UseG1GC 往往能减少跨版本和小容器下的歧义。
  5. 资源:CPU 配额过低很容易放大抖动,是否使用“微容器”还得结合真实压测结果。

参考链接#


  1. The State of Java on Kubernetes 2026 - Akamas, February 2026 ↩︎