为什么Java手动调用GC后空闲内存会大幅减少?
问题解答
你观察到的现象本质是对Runtime.freeMemory()接口的含义误解,加上JDK 16默认垃圾回收器的特性共同导致的,具体原因如下:
- 先明确Runtime内存相关API的定义
Runtime.totalMemory():JVM当前已经从操作系统申请到的堆内存总大小,这个值是动态变化的,会在-Xms(初始堆大小)和-Xmx(最大堆大小)之间浮动Runtime.freeMemory():指上述totalMemory中,尚未被Java对象占用的空闲部分,不是操作系统层面的剩余可用内存Runtime.maxMemory():JVM堆内存允许申请的最大上限,对应启动参数-Xmx的配置值
- 现象的核心原因
JDK 9及之后版本默认使用G1垃圾回收器,且默认开启了自动归还闲置堆内存给操作系统的特性:手动调用gc()触发Full GC后,堆中大量无引用的垃圾对象被清理,堆的空闲占比超过阈值,G1会自动收缩堆的总大小,把多余的内存归还给操作系统。
你测试时GC前JVM已经向OS申请了较大的堆(对应totalMemory很高),所以freeMemory显示为251Mb;GC后JVM把大部分闲置堆内存还给了OS,totalMemory大幅降低,此时计算得到的freeMemory自然就变小了,这不是GC占用了空闲内存,反而是GC清理垃圾后释放了系统资源的表现。 - 验证方案
你可以修改测试代码,把堆总大小也打印出来,就能直观看到变化:
public static void main(String[] args) { Runtime runtime = Runtime.getRuntime(); System.out.println("Total memory before gc: " + runtime.totalMemory() / (1024 * 1024) + " Mb"); System.out.println("Free memory before gc: " + runtime.freeMemory() / (1024 * 1024) + " Mb"); runtime.gc(); System.out.println("Total memory after gc: " + runtime.totalMemory() / (1024 * 1024) + " Mb"); System.out.println("Free memory after gc: " + runtime.freeMemory() / (1024 * 1024) + " Mb"); }
运行后你会发现GC后的totalMemory远低于GC前的数值,通过totalMemory - freeMemory计算实际被对象占用的内存,GC后的占用值一定会比GC前更低,符合垃圾回收的本质逻辑。
如果你不需要JVM自动归还内存给操作系统,可以添加启动参数-XX:MaxHeapFreeRatio=100,此时GC后堆不会收缩,你就能看到freeMemory在GC后上升的预期结果。
内容的提问来源于stack exchange,提问作者zzfima
相关产品推荐
相关产品推荐

