背景:
最近调整了一下线上JVM参数,发现了一些有趣的现象,为什么指定了8G堆区大小,java进程却没有占用8个G,为什么发生了FullGC,物理内存却没有降下去呢?
问题描述:
JVM参数为 -Xms8g -Xmx8g -Xmn4g
堆内存8个G,但是Java进程占用的实际物理内存才2个G,随着进程的运行,占用的物理内存不断的增长。发生FullGC之后,进程所占用的物理内存却没有随之减小,而是稳定在一个范围
问题1:为什么参数Xms指定的内存大小没有被占用
首先我们进行测试,设置 JVM参数为 `-Xmn=1G -Xmx=4G` 堆区总大小为4G, 通过程序不断的new对象,触发GC。通过JConsole可以看到:已提交到操作系统的内存为4G,实际内存随着老年代的增长而增长。
原因分析:
JVM在启动前会向操作系统申请内存, 注意这个内存只是虚拟内存,还没有对应的物理页帧号。随着程序的运行,虚拟内存绑定了更多的物理页帧号,这样就出现了上面的现象。如果-Xms设置的过大,超过了操作系统可分配的内存上线,就会启动失败,报下面的异常,这个异常说明操作系统没有足够的虚拟内存分配给程序了。
Error occurred during initialization of VM
Could not reserve enough space for object heap
下面是一些比较详细深入的解释 [Why does the JVM consume less memory than -Xms specified?](https://stackoverflow.com/questions/12108706/why-does-the-jvm-consume-less-memory-than-xms-specified)
高票回复:You’re looking at the resident memory - that is, the physical RAM
consumed. See here for more info.The virtual memory, however, is the
memory consumed by your application, including the memory swapped out
(to disk). You’ll see there’s a closer correspondance with the virtual
memory and your -Xms settings.See this ServerFault answer for more
info.
问题2:发生完FullGC之后,Java进程的内存却没有随之变小?
如果需要将内存归还给系统,解决方案,添加下面的参数,但是这两个参数当Xms=Xmx时无效
-XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=30
JVM是否将内存释放回操作系统,对于JVM来说是不情愿的,因为调整堆的大小是很昂贵的。因为内存释放完之后,在用的时候还需要重新申请,如果申请的不及时的话,可能会导致OOM,所以在初始化堆的时候都设置堆的初始容量和最大容量一样。
大家可以参考下下面的回答
Java 等语言的 GC 为什么不实时释放内存?