1. 概述

本文将深入解析 JVM 堆内存中的三个关键指标:最大内存(Max)已用内存(Used)已提交内存(Committed) 的区别。

✅ 理解这些概念对排查内存问题、调优 GC 行为和合理设置 -Xmx/-Xms 参数至关重要。
❌ 很多开发者误以为“已用 = 实际占用”,其实背后还有操作系统和 JVM 内存管理的复杂机制。

我们将先了解 JVM 堆的自适应扩容机制,再逐一拆解这三个指标的含义。

2. 最大内存与自适应扩容机制

JVM 堆的大小由两个关键参数控制:

  • -Xms:JVM 启动时的初始堆大小
  • -Xmx:堆内存的最大限制
# 示例:设置初始 512MB,最大 4GB
java -Xms512m -Xmx4g MyApp

⚠️ 如果你不显式设置这两个参数,JVM 会根据以下因素自动选择默认值:

  • 操作系统类型(Windows/Linux/macOS)
  • 物理内存总量
  • JVM 实现版本(HotSpot、OpenJ9 等)

例如,在 64 位服务器版 JVM 上,若物理内存充足,-Xmx 的默认值可能是物理内存的 1/4。

Intial Size

堆内存从 -Xms 指定的初始值开始,随着对象不断分配,JVM 会逐步向操作系统申请更多内存,直到达到 -Xmx 设定的上限。

✅ 简单粗暴地说:

最大堆内存 = -Xmx 设置的值(未设置则用默认值)

这是硬性上限,JVM 堆绝不会超过这个值,否则会抛出 OutOfMemoryError

3. 已用内存(Used)

已用内存指的是当前已被 Java 对象实际占用的堆空间大小

比如你的应用创建了若干个 UserOrder 等对象,这些对象实例所占的内存总和就是“已用内存”。

Used Space

关键点:

  • Used ≤ Max:已用内存不可能超过最大堆内存
  • ✅ GC 会直接影响该值:一次 Full GC 后,已用内存通常会显著下降
  • ⚠️ 即使对象被回收,Used 也不会立刻归零——因为堆可能不会立即收缩(取决于 GC 策略)

示例代码查看当前已用内存:

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;

public class MemoryInfo {
    public static void main(String[] args) {
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        
        long used = heapUsage.getUsed();   // 已用内存(字节)
        long max  = heapUsage.getMax();    // 最大内存(字节)
        
        System.out.println("Used:  " + used / 1024 / 1024 + " MB");
        System.out.println("Max:   " + max / 1024 / 1024 + " MB");
    }
}

输出示例:

Used:  128 MB
Max:   4096 MB

4. 已提交内存(Committed)

这是最容易被误解的一个概念。

已提交内存是 JVM 向操作系统申请并保证可用的物理内存大小。它代表 JVM 当前“手上有多少内存可用”,哪怕还没完全用上。

关键特性:

  • Committed ≥ Used:已提交内存必须大于等于已用内存(否则放不下)
  • ✅ JVM 可以动态向 OS 申请或释放内存(通过 mmap/munmap 等系统调用)
  • ✅ 提交内存可以小于最大内存:JVM 不会一次性把 -Xmx 全部申请下来,而是按需提交

举个例子:

java -Xms128m -Xmx4g MyApp
  • 启动时:Committed = 128MBUsed 可能只有几 MB
  • 随着对象增多:Committed 逐步增长到 512MB、1GB……直到接近 4GB
  • GC 回收后:Used 下降,但 Committed 可能不变(避免频繁系统调用)

用代码查看提交内存:

MemoryUsage heapUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
long committed = heapUsage.getCommitted(); // 已提交内存(字节)
System.out.println("Committed: " + committed / 1024 / 1024 + " MB");

输出示例:

Used:      128 MB
Committed: 512 MB
Max:       4096 MB

📌 一句话总结三者关系

Used ≤ Committed ≤ Max

5. 总结

指标 含义 是否可变 决定因素
Used Java 对象实际占用内存 ✅ 动态变化 对象分配与 GC
Committed JVM 向 OS 申请的可用内存 ✅ 动态申请/释放 JVM 内存管理策略
Max 堆内存上限 ❌ 固定(启动时确定) -Xmx 或默认策略

✅ 实际开发中的建议:

  • 监控 Used / Committed 比例,过高可能预示 GC 压力大
  • 生产环境建议设置 -Xms = -Xmx,避免运行时扩容带来的性能波动
  • 使用 jstat -gc <pid> 或 APM 工具(如 SkyWalking、Prometheus + JVM Exporter)持续观察这三个指标

理解这三个内存指标,能帮你更精准地定位内存泄漏、GC 频繁等问题,而不是一味地“加内存了事”。


原始标题:Difference in Used, Committed, and Max Heap Memory