MemoryUsage是Java中用于描述内存使用情况的工具类,通过它可以获取堆内存、非堆内存的已使用量、总容量等核心指标,计算两者的内存占用比例是评估应用内存健康状态的重要手段。

MemoryUsage核心概念与内存区域划分
MemoryUsage位于java.lang.management包下,每个MemoryUsage实例包含四个核心属性:
- init:内存区域初始分配的容量,单位字节
- used:当前已经被使用的内存容量,单位字节
- committed:保证可供Java虚拟机使用的内存容量,单位字节
- max:内存区域最大可扩展的容量,单位字节,可能为-1表示无限制
Java内存主要分为堆内存和非堆内存两类,两者的作用完全不同:
- 堆内存(Heap Memory):用于存储对象实例,是垃圾回收的主要作用区域,所有通过new创建的对象基本都存放在这里
- 非堆内存(Non-Heap Memory):用于存储类元数据、常量池、方法区、JIT编译后的代码等数据,不属于堆内存管理范围
获取堆与非堆MemoryUsage数据
可以通过ManagementFactory类获取内存管理相关的MXBean,进而拿到堆和非堆的MemoryUsage实例,以下是具体实现代码:
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
public class MemoryUsageAnalyzer {
public static void main(String[] args) {
// 获取内存管理MXBean
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
// 获取堆内存使用情况
MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
// 获取非堆内存使用情况
MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage();
// 打印堆内存信息
System.out.println("堆内存使用情况:");
System.out.println("初始容量:" + heapMemoryUsage.getInit() / 1024 / 1024 + "MB");
System.out.println("已使用容量:" + heapMemoryUsage.getUsed() / 1024 / 1024 + "MB");
System.out.println("已提交容量:" + heapMemoryUsage.getCommitted() / 1024 / 1024 + "MB");
System.out.println("最大容量:" + (heapMemoryUsage.getMax() == -1 ? "无限制" : heapMemoryUsage.getMax() / 1024 / 1024 + "MB"));
// 打印非堆内存信息
System.out.println("n非堆内存使用情况:");
System.out.println("初始容量:" + nonHeapMemoryUsage.getInit() / 1024 / 1024 + "MB");
System.out.println("已使用容量:" + nonHeapMemoryUsage.getUsed() / 1024 / 1024 + "MB");
System.out.println("已提交容量:" + nonHeapMemoryUsage.getCommitted() / 1024 / 1024 + "MB");
System.out.println("最大容量:" + (nonHeapMemoryUsage.getMax() == -1 ? "无限制" : nonHeapMemoryUsage.getMax() / 1024 / 1024 + "MB"));
}
}
计算堆与非堆内存占用比例
计算内存占用比例时,通常建议使用used指标作为已使用量,max作为总容量(如果max为-1则使用committed作为总容量),以下是计算比例的完整代码:
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
public class MemoryRatioCalculator {
public static void main(String[] args) {
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();
// 计算堆内存使用比例
double heapRatio = calculateUsageRatio(heapUsage);
// 计算非堆内存使用比例
double nonHeapRatio = calculateUsageRatio(nonHeapUsage);
System.out.println("堆内存使用比例:" + String.format("%.2f", heapRatio * 100) + "%");
System.out.println("非堆内存使用比例:" + String.format("%.2f", nonHeapRatio * 100) + "%");
// 计算堆与非堆已使用内存的占比
long totalUsed = heapUsage.getUsed() + nonHeapUsage.getUsed();
double heapUsedRatio = (double) heapUsage.getUsed() / totalUsed;
double nonHeapUsedRatio = (double) nonHeapUsage.getUsed() / totalUsed;
System.out.println("堆已使用内存占总已使用内存比例:" + String.format("%.2f", heapUsedRatio * 100) + "%");
System.out.println("非堆已使用内存占总已使用内存比例:" + String.format("%.2f", nonHeapUsedRatio * 100) + "%");
}
private static double calculateUsageRatio(MemoryUsage usage) {
long used = usage.getUsed();
long total = usage.getMax();
// 如果max为-1,使用committed作为总容量
if (total == -1) {
total = usage.getCommitted();
}
if (total <= 0) {
return 0.0;
}
return (double) used / total;
}
}
分析内存占比的注意事项
在分析堆与非堆内存占比时,需要注意以下几点:
- 堆内存占比过高通常意味着对象创建过多,可能存在内存泄漏或者大对象未释放的问题,需要结合GC日志进一步排查
- 非堆内存占比过高常见于类加载过多、常量池膨胀或者JIT编译代码过多的情况,尤其是使用反射、动态代理较多的应用容易出现这类问题
- 不要仅看某一时刻的占比数据,需要持续采集多个时间点的数据,观察占比的变化趋势,才能准确判断内存使用是否正常
- 不同版本的JVM对非堆内存的划分可能存在差异,比如JDK8之后方法区由元空间实现,属于非堆内存,而JDK7及之前方法区属于堆内存的一部分,分析时需要注意JVM版本差异
常见问题解答
为什么非堆内存的max可能是-1
非堆内存中的元空间默认是没有上限的,只受本地内存限制,因此通过MemoryUsage获取元空间对应的非堆内存时,max属性会返回-1,这种情况下计算占比需要使用committed作为总容量。
堆内存占比多少算正常
没有固定的标准,通常建议堆内存使用比例不要长期超过80%,如果长期超过90%说明堆内存容量可能不足,需要考虑调整堆内存参数或者优化应用代码。
注意:调整JVM内存参数时,堆内存通过-Xms(初始堆)、-Xmx(最大堆)设置,非堆内存中的元空间通过-XX:MetaspaceSize、-XX:MaxMetaspaceSize设置,修改后需要重启应用生效。
MemoryUsage堆内存非堆内存内存占用比例JVM内存修改时间:2026-07-07 22:57:29