背景
本人有一台测试服务器,4GB内存,运行MySQL、Jenkins和2个Java应用服务。在某次Java应用的版本升级后,Jenkins由于内存不足闪退。经查,每个Java应用占用了500多MB的内存。
而另一台服务器,2GB内存,运行5个Java应用服务,每个Java应用只占了300多MB的内存。
服务器均使用JDK 1.8,64位环境。
先说结论
Java应用和Jenkins(也是Java程序)启动时添加了-Xms256m
的参数限制,导致Java应用和Jenkins至少要占用256MB的堆内存,而通过Arthas的dashboard
命令查看时,发现实际仅使用80MB左右的堆内存。缺少-Xms
参数后,占用内存减少。
原因
-Xms
指定了Java虚拟机初始(也是最小)堆内存。
生产环境可以使用-Xms
和-Xmx
相同的配置参数减少堆内存伸缩带来的性能消耗,但是在测试环境,尤其是内存不足时,-Xms
可以指定的小一点,只要满足系统基本运行,或者保持默认(据说是系统物理内存的1/64)。
扩展
以下内容基于书和博客内容+自己分析得来,为查证官方资料,仅供参考。
从Arthas的dashboard面板中看到,Java的内存分为heap
和nonheap
,而heap
又分为eden_space
、survivor_space
和old_gen
,nonheap
分为code_cache
、metaspace
、compressed_class_space
、direct
、mapped
。
eden_space
:新生代堆内存,新new的对象在这里。
survivor_space
:新生代堆内存,有2块。在经过gc(复制算法)后,eden_space
和。survivor_sapce
的存活对象会复制到另一块survivor_sapce
中。
old_gen
:老年代堆内存,新生代的对象在多次gc后还活着,就会转移到老年代中。老年代gc使用标记整理算法。
old_gen
:存放在当前物理机环境下虚拟机字节码转换的机器码。
metaspace
:存放静态数据,如Class结构、method的字节码、局部变量表、异常表、参数信息等。
compressed_class_space
:是metaspace
的一部分,在启用指针压缩时有效(Java每个对象都有指向Class的指针),在metaspace
中单独划分一块不大于4GB(默认1GB)的内存,用来存放Class结构,原本所有指向Class结构的64位指针就都可以使用基础偏移+32位指针表示,从而可以减少内存占用。
direct
、mapped
:貌似和nio有关,没有查到相关资料。