这个文章试图解答以下两个困扰许久的问题
- 为什么JVM进程物理内存远大于Xmx设置的值,比如设置了-Xmx512m,用top看到 JVM 实际内存占用超过1G?
- 设置-Xms到底有什么用,设置-Xms=2048m,用top看到JVM占用实际很小?
JVM进程物理内存远大于Xmx
第一个问题,其实很好理解,Xmx指定的是最大堆的大小,也就是我们熟悉的新生代和老生代的最大值,不包括其他(持久代、JIT CodeCache,堆外内存、JNI code、Metaspace、线程栈)
-Xms到底有什么用
第二个问题,会稍微复杂一点
做个简单的实验:
public class Hello { |
关注两个输出
java -cp . -Xms512m -Xmx512m -XX:NativeMemoryTracking=detail Hello |
top输出
top -p $pid |
RES值其实很小
jcmd输出
jcmd 5641 VM.native_memory summary |
上面的输出,也可以解释第一个问题,除了heap的占用,JVM还有很多其他的开销,比如线程、GC等上面所说的
我们来关注一个重要的信息
Java Heap (reserved=524288KB, committed=524288KB) |
我们看到reserved
和committed
都是512M
我们改一下启动参数,把Xms改小到128m
java -cp . -Xms128m -Xmx512m -XX:NativeMemoryTracking=detail Hello |
重新用jcmd来查看一下
Total: reserved=1873968KB, committed=180508KB |
看到reserved的等于Xmx,committed的等于Xms
我们来看一下原理,
为了让资源得到最大合理利用,OS在物理内存之上搞一层虚拟地址,同一台机器上每个进程可访问的虚拟地址空间大小都是一样的,为了屏蔽掉复杂的到物理内存的映射,该工作os直接做了,当需要物理内存的时候,当前虚拟地址又没有映射到物理内存上的时候,就会发生缺页中断,由内核去为之准备一块物理内存,所以即使我们分配了一块1G的虚拟内存,物理内存上不一定有一块1G的空间与之对应
(抄袭于其它博客)
虚拟地址内存的几个状态:
- Free:不解释了
- Reserved:这段地址空间被保留,以便未来进程需要的时候,可以获取到,这部分内存只有在committed以后才可以被访问到
- Committed:可以被进程访问到的内存,意味着在paging file里分配了这么多的page frames,Committed内存只要在进程使用的时候才会被真正加载到主内存,也被称为 on-demand paging
比如我们设置 -Xms512m -Xmx2048m
- Xmx:
JVM告诉操作系统,我的堆可能需要多达2G的内存,你要给我留着,不同意的话,我是有脾气的,我就不启动了。操作系统如果爱你的话,就会承诺,当JVM需要增加堆而尝试分配额外内存的时,这些内存是可以获取到的 - Xms:
JVM启动时分配的初始内存,但是要注意这是committed虚拟地址空间,只有用到的时候,才会变成物理内存
为什么Oracle推荐设置Xmx
和Xms
为相等:
主要是为了避免Xms用完GC以后,需要重新向操作系统申请内存,设置为一样,就省去了这个过程
参考链接
https://blogs.oracle.com/buck/repost%3a-why-is-my-jvm-process-larger-than-max-heap-size
https://plumbr.eu/blog/memory-leaks/why-does-my-java-process-consume-more-memory-than-xmx
https://www.ibm.com/developerworks/library/j-memusage/
http://lovestblog.cn/blog/2015/08/21/rssxmx/