Java程序运行时内存占用需分区域定位:堆、方法区(元空间)、虚拟机栈、本地方法栈、直接内存等,各区域须用Runtime、jstat、jmap、NMT等不同工具针对性监控。

Java 程序运行时的内存占用,本质是 JVM 各内存区域的实际使用情况。查看不能只看“总用了多少”,而要分区域定位:堆、方法区(元空间)、虚拟机栈、本地方法栈、直接内存等。不同区域需用不同手段,有些能实时查,有些需导出分析。
一、用 Runtime 类快速获取堆内存基础指标
适用于代码内简单监控或日志打点,仅反映堆内存(Heap)的使用概况:
- Runtime.totalMemory():当前 JVM 已向操作系统申请的堆内存总量(字节)
- Runtime.freeMemory():当前堆中未被使用的空闲内存(字节)
- Runtime.maxMemory():JVM 堆允许使用的最大内存(即 -Xmx 设置值,字节)
堆使用率可手动计算:1 - freeMemory() / totalMemory();注意 totalMemory() 并非实时等于已分配对象大小,它包含 GC 后未返还给操作系统的空闲块。
二、用 jstat 实时查看各代堆与元空间使用
命令行轻量级工具,无需连接图形界面,适合生产环境定期采样:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- jstat -gc <pid>:显示年轻代(Eden、S0/S1)、老年代、元空间(Metaspace)的容量、已用、GC 次数与耗时
- jstat -gccapacity <pid>:查看各代当前容量上限(如 Eden 当前大小、元空间 committed 大小)
- jstat -metaspace <pid>:专看元空间使用、committed、max 等,区分 used 和 capacity 很关键(used 超过 committed 才真正触发扩容)
例如输出中 MUSE 表示元空间已用大小,MC 是已提交大小——若 MUSE 接近 MC 且持续增长,可能预示元空间将频繁扩容或触发 Full GC。
三、用 jmap 查看堆对象分布与类加载详情
适合排查内存泄漏或大对象堆积,但注意 jmap -histo 会触发 Full GC(加 -live 可避免):
- jmap -histo:live <pid>:列出存活对象的类名、实例数量、总占用字节,按大小倒序,一眼识别“吃内存大户”
- jmap -clstats <pid>:查看每个类加载器加载的类数量及占用空间,辅助判断类加载器泄漏
- jmap -heap <pid>:汇总堆配置(-Xms/-Xmx)、各代实际容量与使用率,比 Runtime 更底层、更准确
常见技巧:把 jmap -histo:live <pid> 输出重定向到文件,用 sort -k3 -nr 按第三列(字节数)排序,快速定位 Top 10 占用类。
四、用 Native Memory Tracking(NMT)监控堆外内存
堆外内存(如 DirectByteBuffer、JIT 代码缓存、线程栈本身)不走 GC,传统工具看不到。启用 NMT 后才能追踪:
- 启动 JVM 时加参数:-XX:NativeMemoryTracking=summary(或 detail)
- 运行中执行:jcmd <pid> VM.native_memory summary,查看 total、Java Heap、Class、Thread、Internal、Direct 等分类内存用量
- 对比两次输出差值,可发现 DirectByteBuffer 分配未释放、线程数暴涨导致栈内存飙升等问题
特别注意:-Xss 设置的是每个线程栈大小,NMT 中 Thread 分类的用量 ≈ 线程数 × -Xss,若该值异常高,先检查线程是否泄漏。

















