运行性能测试时Runtime报负内存占用的原因咨询
关于性能测试中负内存占用的原因及解决方案
嘿,我来帮你理清这个问题~ 先解释为什么会出现负内存占用,再给你调整代码的建议,方便输出CSV格式的数据直接导入Excel使用。
为什么会出现负内存占用?
你遇到的负内存占用问题,核心原因是 Runtime.totalMemory() 和 Runtime.freeMemory() 的调用不是原子操作,中间存在时间窗口,JVM的内存状态可能已经发生了变化:
- 当你先调用
totalMemory()获取当前JVM已分配的堆总量后,在调用freeMemory()之前,JVM可能触发了垃圾回收(GC),甚至因为堆空闲过多自动收缩了堆的总大小。 - 这时候
freeMemory()返回的是收缩后堆中的空闲内存,这个值可能会大于你之前获取的totalMemory()值,导致totalMemory() - freeMemory()的计算结果为负。
举个具体场景:
- 你调用
totalMemory()得到100MB - JVM触发Full GC回收大量内存,同时把堆收缩到60MB,此时堆中空闲内存为55MB
- 你接着调用
freeMemory()得到55MB - 用之前的100MB减去55MB,看起来结果是正的,但如果JVM在两次调用间的内存调整更极端,就可能出现
totalMemory()旧值小于freeMemory()新值的情况,最终算出负数。
正确的内存统计与CSV输出方案
1. 替换Runtime为MemoryMXBean(更可靠)
MemoryMXBean 可以原子性地获取堆内存的使用快照,彻底避免竞态条件导致的负数问题。示例代码:
import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; // 获取内存管理Bean MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); // 测试前记录内存使用 MemoryUsage preHeapUsage = memoryBean.getHeapMemoryUsage(); long preUsedMemory = preHeapUsage.getUsed(); // 记录测试开始时间 long startTime = System.nanoTime(); // 执行你的测试逻辑 runYourTest(); // 替换成你的测试方法 // 测试后记录耗时与内存使用 long elapsedTime = System.nanoTime() - startTime; MemoryUsage postHeapUsage = memoryBean.getHeapMemoryUsage(); long postUsedMemory = postHeapUsage.getUsed(); // 计算内存变化量(可选) long memoryDelta = postUsedMemory - preUsedMemory;
2. 输出逗号分隔的CSV格式数据
要方便复制到Excel,你可以直接按行输出逗号分隔的字段,第一次测试时输出表头,后续每次测试输出一行数据:
// 首次测试时输出表头 System.out.println("测试序号,执行时间(纳秒),测试前内存占用(字节),测试后内存占用(字节),内存变化(字节)"); // 循环测试时递增序号 int testNum = 1; // 每次测试输出一行数据 System.out.printf("%d,%d,%d,%d,%d%n", testNum, elapsedTime, preUsedMemory, postUsedMemory, memoryDelta); testNum++;
直接复制控制台的输出内容,粘贴到Excel中,就能自动按列拆分数据。
额外建议
- 如果测试后出现内存变化为负,大概率是测试过程中JVM触发了GC回收了测试产生的临时对象,这是正常现象。你可以在测试前尝试触发GC(
System.gc();)来减少这种波动,但注意强制GC不保证一定会执行。 - 性能测试建议多次重复取平均值,单次结果的波动是正常的,多跑几组数据更有参考价值。
内容的提问来源于stack exchange,提问作者Erik
相关产品推荐
相关产品推荐

