JVM-合理配置堆内存

我们增加多少内存比较合适?如果内存过大,那么如果产生FullGC的时候,GC时间会相对比较长,如果内存较小,那么就会频繁的触发GC(吞吐量低),在这种情况下,我们该如何合理的适配堆内存大小呢?

推荐配置

  1. 通常会将-Xms和-Xmx两个参数配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能
  2. Java整个堆大小设置,Xmx和Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍。
  3. 方法区(永久代PermSize和MaxPermSize或元空间MetaspaceSize和 MaxMetaspaceSize)设置为老年代存活对象的1.2-1.5倍。
  4. 年轻代Xmn的设置为老年代存活对象的1-1.5倍。

如何计算老年代存活对象

方式1:查看日志

-Xms60m -Xmx60m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -Xloggc:./logs/gc.log

JVM参数中添加GC日志,GC日志中会记录每次FullGC之后各代的内存大小,观察老年代GC之后的空间大小。可观察一段时间内(比如2天)的FullGC之后的内存情况,根据多次的FullGC之后的老年代的空间大小数据来预估FullGC之后老年代的存活对象大小(可根据多次FullGC之后的内存大小取平均值)。

方式2:强制触发FullGc

  • 方式1的方式比较可行,但需要更改JVM参数,并分析日志。同时,在使用CMS回收器的时候,有可能不能触发FullGC,所以日志中并没有记录FullGC的日志。在分析的时候就比较难处理。所以,有时候需要强制触发一次FullGC,来观察FullGC之后的老年代存活对象大小。
  • 注:强制触发FullGC,会造成线上服务停顿(STW),要谨慎!建议的操作方式为,在强制FullGC前先把服务节点摘除,FullGC之后再将服务挂回可用节点,对外提供服务,在不同时间段触发FullGC,根据多次FullGC之后的老年代内存情况来预估FullGC之后的老年代存活对象大小。

触发FullGC:

  1. jmap -histo:live <pid> 打印每个class的实例数目,内存占用,类全名信息.live子参数加上后,只统计活的对象 数量。此时会触发FullGC
  2. jmap -dump:live,format=b,fle=heap.bin <pid> 将当前的存话对象dump到文件,此时会触发FullGC在性能测试环境,可以通过java监控工具来触发FullGC,比如使用VisualVM和JConsole,
  3. VisualVM集成了JConsole,VisualVM或者JConsole上面有一个触发GC的按钮。

演示过程

PS D:\mydesign\code\linux> jps
26064 
28272 Launcher
5376 WebSpringApplication
21076 
18360 RemoteMavenServer36
12124 RemoteMavenServer36
28732 Jps
PS D:\mydesign\code\linux> 
PS D:\mydesign\code\linux> jstat -gc 5376 1000 5
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
34816.0 34816.0  0.0    0.0   279552.0 132949.4  699392.0   15175.3   21296.0 20315.1 2864.0 2660.3      1    0.009   1      0.021    0.031
34816.0 34816.0  0.0    0.0   279552.0 132949.4  699392.0   15175.3   21296.0 20315.1 2864.0 2660.3      1    0.009   1      0.021    0.031
34816.0 34816.0  0.0    0.0   279552.0 132949.4  699392.0   15175.3   21296.0 20315.1 2864.0 2660.3      1    0.009   1      0.021    0.031
34816.0 34816.0  0.0    0.0   279552.0 132949.4  699392.0   15175.3   21296.0 20315.1 2864.0 2660.3      1    0.009   1      0.021    0.031
34816.0 34816.0  0.0    0.0   279552.0 132949.4  699392.0   15175.3   21296.0 20315.1 2864.0 2660.3      1    0.009   1      0.021    0.031
jmap -histo:live 5376
jmap -heap 5376

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 1073741824 (1024.0MB)
   NewSize                  = 357564416 (341.0MB)
   MaxNewSize               = 357564416 (341.0MB)
   OldSize                  = 716177408 (683.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 286261248 (273.0MB)
   used     = 7013424 (6.6885223388671875MB)
   free     = 279247824 (266.3114776611328MB)
   2.4500081827352336% used
From Space:
   capacity = 35651584 (34.0MB)
   used     = 0 (0.0MB)
   free     = 35651584 (34.0MB)
   0.0% used
To Space:
   capacity = 21495808 (20.5MB)
   used     = 0 (0.0MB)
   free     = 21495808 (20.5MB)
   0.0% used
PS Old Generation
   capacity = 716177408 (683.0MB)
   used     = 10939672 (10.432884216308594MB)
   free     = 705237736 (672.5671157836914MB)
   1.527508670030541% used

15443 interned Strings occupying 1389816 bytes.

估算GC频率

        正常情况我们应该根据我们的系统来进行一个内存的估算,这个我们可以在测试环境进行测试,最开始可以将内存设置的大一些,比如4G这样,当然这也可以根据业务系统估算来的。

        比如从数据库获取一条数据占用128个字节,需要获取1000条数据,那么一次读取到内存的大小就是128B/1024Kb/1024M)*1000=0.122M,那么我们程序可能需要并发读取,比如每秒读取100次,那么内存占用就是0.122*100=12.2M,如果堆内存设置1G,那么年轻代大小大约就是333M,那么333M*80%/12.2M=21.84s,也就是说我们的程序几乎每分钟进行两到三次youngGC。这样可以让我们对系统有一个大致的估算。

 新生代与老年代的比例

使用ParallelGC的情况下,不管是否开启了UseAdaptiveSizePolicy参数,默认Eden与Survivor的比例都为:6:1:1

-XX:+PrintGCDetails   -XX:+PrintGCDateStamps  -Xms300M -Xmx300M  -Xloggc:10g/gc.log

新生代(Young)与老年代(01d)的比例为1:2,所以,内存分配应该是新生代100M,老年代200M

jmap -heap 3725

参数AdaptiveSizePolicy

开启:-XX:+UseAdaptiveSizePolicy

关闭:-XX:-UseAdaptiveSizePolicy

  1. 在JDK1.8中,如果使用CMS,无论UseAdaptiveSizePolicy如何设置,都会将

UseAdaptiveSizePolicy设置为false:不过不同版本的JDK存在差异

  1. UseAdaptiveSizePolicy不要和SurvivorRatio参数显示设置搭配使用,一起使用会导致参数失效
  2. 由于UseAdaptivesizePolicy会动态调整Eden、Survivor的大小,有些情况存在 Survivor被自动调为很小,比如十几MB甚至几MB的可能,这个时候YGc回收掉Eden区后,还存活的对象进入Survivor装不下,就会直接晋升到老年代,导致老年代占用空间逐渐增加,从而触发FULL GC,如果一次FULL GC的耗时很长(比如到达几百毫秒),那么在要求高响应的系统就是不可取的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值