runtime.totalmemory()返回jvm已从操作系统申请的堆内存总量(committed memory),非实际使用量,受-xms/-xmx、gc策略等影响,宜配合freememory()和maxmemory()定期采样监控。

Runtime.totalMemory() 返回的是 JVM 当前已从操作系统申请到的堆内存总量(单位字节),但它**不是当前实际使用的内存**,而是 JVM 向系统“要来但未必全用上”的内存池大小。它反映的是 JVM 堆的已分配容量(committed memory),会随 GC 扩容/收缩而动态变化,适合观察 JVM 内存申请行为的趋势,但不能直接等同于“程序用了多少内存”。
理解 totalMemory() 的真实含义
该值 ≈ JVM 堆的 committed memory(已提交内存),即已由 OS 分配、可被 JVM 立即用于对象分配的物理内存。它受以下因素影响:
- JVM 启动参数(如 -Xms 和 -Xmx):-Xms 决定初始 committed 值;-Xmx 是上限
- 垃圾回收器行为:例如 G1 或 ZGC 在扩容或回收后可能释放部分 committed 内存(尤其开启 -XX:+UseG1GC -XX:+G1UseAdaptiveIHOP 等时)
- 是否启用 -XX:+AlwaysPreTouch:若开启,JVM 启动时就触达全部 -Xms,totalMemory() 初始即为 -Xms 值
如何安全、有效地监控其变化
建议以固定间隔采样并记录差值,避免高频调用干扰 GC 或产生噪声:
- 用 ScheduledExecutorService 每 1~5 秒读取一次:
Runtime.getRuntime().totalMemory() - 同时记录 Runtime.freeMemory() 和 Runtime.maxMemory(),三者结合更全面(例如计算已提交堆使用率:
(total - free) / (double)total) - 注意:不要在 GC 循环中或性能敏感路径(如 hot loop)里频繁调用,它虽轻量但非零开销
- 示例片段:
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor(); monitor.scheduleAtFixedRate(() -> { long total = Runtime.getRuntime().totalMemory(); long free = Runtime.getRuntime().freeMemory(); long used = total - free; System.out.printf("Committed: %s MB, Used in committed: %s MB%n", total / 1024 / 1024, used / 1024 / 1024); }, 0, 2, TimeUnit.SECONDS);
区分 totalMemory() 与其它关键内存指标
避免混淆,明确各方法用途:
- totalMemory() → 已提交堆内存(committed heap)
- freeMemory() → 当前 committed 区域中未被占用的部分(注意:不是整个堆空闲,只是已提交区内的空闲)
- maxMemory() → 当前堆最大可扩展上限(≈ -Xmx,但可能因 GC 策略略低)
-
jstat -gc
或 JMX 的 MemoryUsage.getCommitted()→ 更权威、可区分新生代/老年代的 committed 数据 - VisualVM / JConsole / Prometheus + JMX exporter → 推荐用于生产环境长期趋势分析,比轮询 totalMemory() 更稳定可靠
典型变化场景与解读
观察 totalMemory() 波动可辅助诊断 JVM 行为:
- 启动后缓慢上升:JVM 按需扩容(如 -Xms=256m -Xmx=2g,初始 total≈256MB,负载增加后逐步涨至 1.2GB)
- GC 后明显下降:某些 GC 策略(如 Serial、Parallel)在回收后会尝试缩小 committed 堆(尤其当空闲率持续高于阈值)
- 长时间持平无波动:可能 -Xms == -Xmx,或应用内存趋于稳定;也可能是监控频率太低错过变化
- 突增后不回落:疑似内存泄漏前期表现(配合 used memory 一起看),或突发大对象分配触发扩容
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











