JVM内存设置多⼤合适?Xmx和Xmn如何设置?
JVM内存设置多⼤合适?Xmx和Xmn如何设置?
问题:
新上线⼀个java服务,或者是RPC或者是WEB站点,内存的设置该怎么设置呢?设置成多⼤⽐较合适,既不浪费内存,⼜不影响性能呢?
分析:
依据的原则是根据Java Performance⾥⾯的推荐公式来进⾏设置。
296d1509689688.png
具体来讲:
Java整个堆⼤⼩设置,Xmx 和 Xms设置为⽼年代存活对象的3-4倍,即FullGC之后的⽼年代内存占⽤的3-4倍
永久代 PermSize和MaxPermSize设置为⽼年代存活对象的1.2-1.5倍。
年轻代Xmn的设置为⽼年代存活对象的1-1.5倍。
⽼年代的内存⼤⼩设置为⽼年代存活对象的2-3倍。
BTW:
1、Sun官⽅建议年轻代的⼤⼩为整个堆的3/8左右,所以按照上述设置的⽅式,基本符合Sun的建议。
2、堆⼤⼩=年轻代⼤⼩+年⽼代⼤⼩,即xmx=xmn+⽼年代⼤⼩。 Permsize不影响堆⼤⼩。
3、为什么要按照上⾯的来进⾏设置呢?没有具体的说明,但应该是根据多种调优之后得出的⼀个结论。
如何确认⽼年代存活对象⼤⼩?
⽅式1(推荐/⽐较稳妥):
JVM参数中添加GC⽇志,GC⽇志中会记录每次FullGC之后各代的内存⼤⼩,观察⽼年代GC之后的空间⼤⼩。可观察⼀段时间内(⽐如2天)的FullGC之后的内存情况,根据多次的FullGC之后的⽼年代的空间⼤⼩数据来预估FullGC之后⽼年代的存活对象⼤⼩(可根据多次FullGC之后的内存⼤⼩取平均值)
⽅式2:(强制触发FullGC, 会影响线上服务,慎⽤)
⽅式1的⽅式⽐较可⾏,但需要更改JVM参数,并分析⽇志。同时,在使⽤CMS回收器的时候,有可能不能触发FullGC(只发⽣CMS GC),所以⽇志中并没有记录FullGC的⽇志。在分析的时候就⽐较难处理。
BTW:使⽤jstat -gcutil⼯具来看FullGC的时候, CMS GC是会造成2次的FullGC次数增加。具体可参见之前写的⼀篇关于jstat使⽤的⽂章所以,有时候需要强制触发⼀次FullGC,来观察FullGC之后的⽼年代存活对象⼤⼩。
注:强制触发FullGC,会造成线上服务停顿(STW),要谨慎,建议的操作⽅式为,在强制FullGC前先把服务节点摘除,FullGC之后再将服务挂回可⽤节点,对外提供服务
在不同时间段触发FullGC,根据多次FullGC之后的⽼年代内存情况来预估FullGC之后的⽼年代存活对象⼤⼩
如何触发FullGC ?
使⽤jmap⼯具可触发FullGC
jmap -dump:live,format=b,file=heap.bin <pid> 将当前的存活对象dump到⽂件,此时会触发FullGC
jmap -histo:live <pid> 打印每个class的实例数⽬,内存占⽤,类全名信息.live⼦参数加上后,只统计活的对象数量. 此时会触发FullGC
具体操作实例:
以我司的⼀个RPC服务为例。
BTW:刚上线的新服务,不知道该设置多⼤的内存的时候,可以先多设置⼀点内存,然后根据GC之后的情况来进⾏分析。
初始JVM内存参数设置为: Xmx=2G Xms=2G xmn=1G
使⽤jstat 查看当前的GC情况。如下图:
23b61509689687.png
YGC平均耗时: 173.825s/15799=11ms
FGC平均耗时:0.817s/41=19.9ms
平均⼤约10-20s会产⽣⼀次YGC
看起来似乎不错,YGC触发的频率不⾼,FGC的耗时也不⾼,但这样的内存设置是不是有些浪费呢?
为了快速看数据,我们使⽤了⽅式2,产⽣了⼏次FullGC,FullGC之后,使⽤的jmap -heap 来看的当前的堆内存情况(也可以根据GC⽇志来看)
heap情况如下图:(命令: jmap -heap <pid>)
f9c71509689687.png
上图中的concurrent mark-sweep generation即为⽼年代的内存描述。
⽼年代的内存占⽤为100M左右。按照整个堆⼤⼩是⽼年代(FullGC)之后的3-4倍计算的话,设置各代的内存情况如下:
Xmx=512m Xms=512m Xmn=128m PermSize=128m ⽼年代的⼤⼩为(512-128=384m)为⽼年代存活对象⼤⼩的3倍左右
调整之后的,heap情况
aaaaa689686.png
GC情况如下:
jvm调优参数bbbbb689686.png
YGC 差不多在10s左右触发⼀次。每次YGC平均耗时⼤约9.41ms。可接受。
FGC平均耗时:0.016s/2=8ms
整体的GC耗时减少。但GC频率⽐之前的2G时的要多了⼀些。
注:看上述GC的时候,发现YGC的次数突然会增多很多个,⽐如从1359次到了1364次。具体原因是?
总结:
在内存相对紧张的情况下,可以按照上述的⽅式来进⾏内存的调优,到⼀个在GC频率和GC耗时上都可接受的⼀个内存设置,可以⽤较⼩的内存满⾜当前的服务需要
但当内存相对宽裕的时候,可以相对给服务多增加⼀点内存,可以减少GC的频率,GC的耗时相应会增加⼀些。⼀般要求低延时的可以考虑多设置⼀点内存,对延时要求不⾼的,可以按照上述⽅式设置较⼩内存。
补充:
永久代(⽅法区)并不在堆内,所以之前有看过⼀篇⽂章中描述的整个堆⼤⼩=年轻代+年⽼代+永久代的描述是不正确的。
转⾃:
补充
-verbose:gc 现实垃圾收集信息
-Xloggc:gc.log 指定垃圾收集⽇志⽂件
-Xmn:young generation的heap⼤⼩,⼀般设置为Xmx的3、4分之⼀
-XX:SurvivorRatio=2 :⽣还者池的⼤⼩,默认是2,如果垃圾回收变成了瓶颈,您可以尝试定制⽣成池设置
-XX:NewSize: 新⽣成的池的初始⼤⼩。缺省值为2M。
-XX:MaxNewSize: 新⽣成的池的最⼤⼤⼩。缺省值为32M。
+XX:AggressiveHeap 会使得 Xms没有意义。这个参数让jvm忽略Xmx参数,疯狂地吃完⼀个G物理内存,再吃尽⼀个G的swap。
-Xss:每个线程的Stack⼤⼩,“-Xss 15120” 这使得JBoss每增加⼀个线程(thread)就会⽴即消耗15M内存,⽽最佳值应该是128K,默认值好像是512k.