设置JVM内存的参数有四个:
-Xmx Java Heap最大值,默认值为物理内存的1/4,最佳设值应该视物理内存大小及计算机内其他内存开销而定,建议设置为可用内存的最大值的80%;
-Xms Java Heap初始值,默认值为物理内存的1/64,Server端JVM最好将-Xms和-Xmx设为相同值;
-Xmn Java Heap Young区大小,默认值为堆内存的3/8,对于最新的G1收集器,建议不要设置此参数,而由系统自动分配和管理;
-Xss 每个线程的Stack大小。
JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指 定,默认是物理内存的1/4。默认空余堆内存小于 40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、 -Xmx相等以避免在每次GC 后调整堆的大小,因为这种调整也是消耗资源的。
初始化堆的大小是JVM在启动时向系统申请的内存的大小。一般而言,这个参数不重要。但是有的应用程序在大负载的情况下会急剧地占用更多的内存,此时这个参数就是显得非常重要,如果JVM启动时设置使用的内存比较小,而在这种情况下有许多对象进行初始化,JVM就必须重复地增加内存来满足使用。由于这种原因,我们一般把-Xms和-Xmx设为一样大,而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象,内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时JVM就会提示内存溢出,并且导致应用服务崩溃。所以,如果Xms超过了Xmx值,或者堆最大值和非堆最大值的总和超过了物理内存或者操作系统的最大限制都会引起服务器启动不起来。
JVM的堆大小决定了JVM花费在收集垃圾上的时间和频度。收集垃圾可以接受的速度与应用有关,应该通过分析实际的垃圾收集的时间和频率来调整。如果堆的大小很大,那么完成一次FUll GC就会很慢,但是频度会降低。如果把堆的大小调小,完成一次FUll GC就变快,但是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为保证最好的性能,要把堆的大小设大,保证垃圾收集不在整个基准测试的过程中出现。如果系统花费很多的时间收集垃圾,请减小堆大小。一次FUll GC应该不超过 3-5 秒。如果垃圾收集成为瓶颈,那么需要指定堆的大小,检查垃圾收集的详细输出,研究垃圾收集参数对性能的影响。一般说来,你应该使用物理内存的 80% 作为堆大小。
关于-Xmn的设置,一种说法是应为-Xmx的1/4,也有人根据经验提出,为了优化GC,最好让-Xmn值约等于-Xmx的1/3。官方建议年轻代的大小为-Xmx的25%到50%之间,默认为3/8左右。
如果没有设置-Xmn,新生代和老年代将根据默认比例(1:2)分配内存,可以通过NewRadio来调整,但最多也就调为1:1。如果需要调更大比例,-XX:newSie、-XX:MaxNewSie来设置其绝对大小。同样为了防止新生代在最大和最小之间的堆收缩,产生额外的消耗,通常这两个值也设为同样的大小。
HotSpot JVM把新生代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1,也就是Eden区占新生代的8/10,from区和to区各占1/10。为啥默认会是这个比例,因为年轻代中的对象基本都是朝生夕死的(80%以上)。
可以根据Java Performance里面的推荐公式来进行设置:
1.Java整个堆大小设置,Xmx 和 Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍。
2.永久代 PermSize和MaxPermSize设置为老年代存活对象的1.2-1.5倍。(注:JDK1.8之后改为元空间,改为使用直接内存而不用虚拟机内存了,但GC还是可以进行回收。虚拟机参数名称也改为MetaspaceSize和MaxMetaspaceSize)
3.年轻代Xmn的设置为老年代存活对象的1-1.5倍。
4.老年代的内存大小设置为老年代存活对象的2-3倍。
如何确认老年代存活对象大小?
可以通过GC日志。JVM参数中添加GC日志,GC日志中会记录每次FullGC之后各代的内存大小,观察老年代GC之后的空间大小。可观察一段时间内(比如2天)的FullGC之后的内存情况,根据多次的FullGC之后的老年代的空间大小数据来预估FullGC之后老年代的存活对象大小(可根据多次FullGC之后的内存大小取平均值)。
简单总结一下,-Xmx 应该使用可用物理内存的 80% 作为堆大小,-Xmn一般为-Xmx的1/4到1/3,永久代 PermSize和MaxPermSize设置大小和-Xmx差不多。