jvm性能测试关键看堆内存动态行为与资源承诺匹配:关注used/committed差值、新生代存活率、老年代增长趋势;三要素为used(实际使用)、committed(已申请)、max(硬上限);新生代高频ygc正常,老年代ou持续上升且fgc回收少提示泄漏;metaspace持续增长或线程数超300易致oom;gct占比超10%须分析对象分布而非盲目扩堆。

JVM性能测试中,内存指标不是看“用了多少”,而是看“怎么用”——关键在堆内存的动态行为与资源承诺之间的匹配关系。真正影响稳定性和响应延迟的,往往是used和committed的差值、新生代对象存活率、老年代持续增长趋势,而非单纯的内存占用百分比。
堆内存三要素:used、committed、max
这三个值构成JVM内存健康判断的基础骨架:
- Heap Used:当前实际分配并正在使用的堆内存量。它会随对象创建/回收剧烈波动,像锯齿一样上下跳动是正常现象;但若长期缓慢爬升不回落,大概率存在内存泄漏。
- Heap Committed:JVM已向操作系统申请、保证可用的堆内存。这个值一般只增不减,除非发生Full GC并触发堆收缩(极少默认启用)。它代表JVM“已占位”的资源,直接影响容器或宿主机的内存压力感知。
-
Heap Max:由
-Xmx设定的硬上限。一旦used接近max且频繁触发GC,说明堆容量不足或对象生命周期设计不合理;若committed远低于max,则可能初始堆(-Xms)设得太小,导致运行中反复扩容,带来STW开销。
新生代与老年代的分工信号
年轻代(Young Gen)和老年代(Old Gen)的使用模式,直接反映应用对象生命周期是否合理:
- Eden区持续快速填满 + Survivor区反复交换 → 表明大量短生命周期对象产生,Minor GC频率高但属正常;若Survivor区总有一边长期淤积,可能是
-XX:SurvivorRatio配置失衡或对象晋升过早。 - 老年代OU(Old Used)缓慢但持续上升,且每次Full GC后回收很少 → 是典型内存泄漏或缓存未及时清理的信号;若OU在Full GC后仍高于70%,需检查大对象直接入老年代(如超过
-XX:PretenureSizeThreshold)、或长期存活对象堆积。 - YGC次数多但YGCT总时间低,而FGC次数少但FGCT单次超200ms → 说明Minor GC高效,但老年代回收成本极高,应优先优化对象晋升策略或考虑G1/ZGC等低停顿收集器。
元空间与线程内存:容易被忽略的本地内存消耗
堆外内存同样会拖垮稳定性,尤其在容器环境:
-
Metaspace Used持续增长,接近
-XX:MaxMetaspaceSize阈值 → 多见于频繁动态生成类(如Spring CGLIB代理、Groovy脚本、OSGi热部署),需限制元空间上限并监控类加载数(loadedClassCount)。 -
Thread Count超过300且峰值不断刷新 → 可能存在线程池未复用、异步任务无界提交、或连接泄漏;每个线程默认栈大小(
-Xss)为1MB,500个线程就占用500MB本地内存,极易触发OOM(unable to create new native thread)。 - 通过
jstat -gc看到MC(Metaspace Capacity)与MU(Metaspace Used)比值长期>90%,或jcmd <pid> VM.native_memory summary</pid>显示Class区域committed异常高,都提示元空间压力已临界。
GC统计指标:不只是次数和时间
GC日志或jstat -gc输出中的数字,要结合上下文读出真实问题:
-
YGC每秒发生多次,但YGCT累计占比<1% → 吞吐尚可,无需干预;若YGCT占比突然跃升至5%以上,需查是否出现大对象分配、或Survivor空间不足导致提前晋升。 -
FGC次数低但FGCT单次>1s,且OU回收后仅下降5%~10% → 老年代碎片化严重,G1可能需要调大-XX:G1HeapRegionSize,ZGC/Shenandoah则更适应此类场景。 -
GCT(总GC时间)占应用总运行时间比例>10% → 不论是Young还是Full,都表明GC已成为性能瓶颈,此时必须结合堆dump(jmap -histo:live)分析对象分布,而非盲目加大堆内存。











