你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

JVM配置、监控、调优

2021/12/22 15:06:25

1.JVM资料

java虚拟机设置

#xms虚拟机最小内存 xmx虚拟机最大内存 xmn新生代初始内存(比NewRatio优先)
-Xms256m -Xmx256m -Xmn192M 

#老年代和新生代比例,默认2
-XX:NewRatio=2

#禁用Survivor区自适应策略
-XX:-UseAdaptiveSizePolicy

#Survivor区使用率,默认50%
-XX:TargetSurvivorRatio=80

#调整Eden和survivor的比例,默认8
-XX:SurvivorRatio=4

#设定CMS在对内存占用率达到70%的时候开始GC
-XX:CMSInitiatingOccupancyFraction=75

#只是用设定的回收阈值(上面指定的70%),如果不指定,JVM仅在第一次使用设定值,后续则自动调整.
-XX:+UseCMSInitiatingOccupancyOnly

java虚拟机监控

#java虚拟机堆内存分配情况
jmap -heap 3965

#java虚拟机垃圾回收情况
jstat -gcutil 7924 1000

#查看java进程对象内存占用排名前20
jmap -histo:live 16247 |head -n 20

#查看对象数最多的对象,按降序输出
jmap -histo pid | sort -k 2 -g -r 

#查看内存的对象,按降序输出
jmap -histo pid | sort -k 3 -g -r 

2.环境

         腾讯云ECS 1核1G1MB环境,spring boot 2.3,上面有个java程序smartfinancialmanager.jar,主要用于爬取别的网址上信息并展示,未使用数据库。

3.jvm运行情况

        JVM常规配置 -server -Xms256m -Xmx256m -XX:+UseSerialGC,jmap -heap 4943。

Server compiler detected.
JVM version is 25.232-b09

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 268435456 (256.0MB)
   NewSize                  = 89456640 (85.3125MB)
   MaxNewSize               = 89456640 (85.3125MB)
   OldSize                  = 178978816 (170.6875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 80543744 (76.8125MB)
   used     = 33062984 (31.53131866455078MB)
   free     = 47480760 (45.28118133544922MB)
   41.049723241075064% used
Eden Space:
   capacity = 71630848 (68.3125MB)
   used     = 24150088 (23.03131866455078MB)
   free     = 47480760 (45.28118133544922MB)
   33.714647633377176% used
From Space:
   capacity = 8912896 (8.5MB)
   used     = 8912896 (8.5MB)
   free     = 0 (0.0MB)
   100.0% used
To Space:
   capacity = 8912896 (8.5MB)
   used     = 0 (0.0MB)
   free     = 8912896 (8.5MB)
   0.0% used
tenured generation:
   capacity = 178978816 (170.6875MB)
   used     = 36941368 (35.23003387451172MB)
   free     = 142037448 (135.45746612548828MB)
   20.64007843252243% used

18988 interned Strings occupying 1772640 bytes.

        默认使用Mark Sweep Compact GC(标记清理压缩回收器),其中老年代与新生代的比值为默认的2,老年代占堆内存为2/3约170M,新生代占堆内存约1/3约85M。

        监控java进程堆内存增长情况每隔4秒打印一次(E为新生代中的Eden区域,S0/S1为两个存活区,O为老年代),jstat -gcutil 9559 4000。

[root@VM_0_12_centos ~]# jstat -gcutil 13531 4000
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00 100.00  79.78  22.95  94.28  90.40     25    0.307     2    0.192    0.499
  0.00 100.00  85.61  22.95  94.28  90.40     25    0.307     2    0.192    0.499
  0.00 100.00  91.45  22.95  94.28  90.40     25    0.307     2    0.192    0.499
  0.00 100.00  95.33  22.95  94.28  90.40     25    0.307     2    0.192    0.499
100.00   0.00   2.13  23.89  94.29  90.43     26    0.316     2    0.192    0.509
100.00   0.00   8.02  23.89  94.29  90.43     26    0.316     2    0.192    0.509
100.00   0.00  11.94  23.89  94.29  90.43     26    0.316     2    0.192    0.509

3.分析及调优

3.1 Survivor区100%过早晋升

        观察上图,可以看到S0、S1区经常为100%,这说明存活区太小了。如果为100%,每次eden区满了之后,那么从 Eden 存活下来的和原来在 Survivor 空间中不够老的对象占满 Survivor 后, 就会提升到老年代,能够看到这一轮 Minor GC 后老年代由原来的 22.95 占用变成了 23.89 占用, 这属于一个典型的 JVM 内存问题。 称为 "premature promotion"(过早提升)。

        优化:加大S0/S1空间大小。解决方法有3种:第一:机器内存够大,直接增大xms;第二:调整老年代和新生代的比例(-XX:NewRatio);第三:不调整NewRatio比例,增大新生代大小(S0/S1随着增大);第三:调整eden和S0/S1的比例(-XX:SurvivorRatio)。

        由于机器内存有限,使用后两种方法优化。先使用第二种方法,将老年代和新生代的比例由默认的2调整为1(最大只能调整到1),即新生代占xms的1/2,由于eden与S0/S1的默认比例为8,S0/S1即为xms的1/20,256/20≈12.8M。

        java运行参数为:-server -Xms256m -Xmx256m -XX:NewRatio=1,运行jmap -heap 21238查看堆内存分配如下:

Server compiler detected.
JVM version is 25.232-b09

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 268435456 (256.0MB)
   NewSize                  = 134217728 (128.0MB)
   MaxNewSize               = 134217728 (128.0MB)
   OldSize                  = 134217728 (128.0MB)
   NewRatio                 = 1
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 120848384 (115.25MB)
   used     = 69269144 (66.0602035522461MB)
   free     = 51579240 (49.189796447753906MB)
   57.31904863535453% used
Eden Space:
   capacity = 107479040 (102.5MB)
   used     = 69269144 (66.0602035522461MB)
   free     = 38209896 (36.439796447753906MB)
   64.44897907536205% used
From Space:
   capacity = 13369344 (12.75MB)
   used     = 0 (0.0MB)
   free     = 13369344 (12.75MB)
   0.0% used
To Space:
   capacity = 13369344 (12.75MB)
   used     = 0 (0.0MB)
   free     = 13369344 (12.75MB)
   0.0% used
tenured generation:
   capacity = 134217728 (128.0MB)
   used     = 16895496 (16.11280059814453MB)
   free     = 117322232 (111.88719940185547MB)
   12.588125467300415% used

15914 interned Strings occupying 1560552 bytes.

         从上图可以看到From Space/To Space(S0/S1)的大小为12.75M,预前面的计算12.8M近似。执行jstat -gcutil 21238 4000命令,监控堆内存变化(下图),发现S0/S1还是出现100%,这表明S0/S1区大小还是不够。

[root@VM_0_12_centos ~]# jstat -gcutil 21238 4000
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00 100.00  95.38  34.40  93.66  90.09     62    0.958     3    0.248    1.206
  0.00 100.00  99.36  34.40  93.66  90.09     62    0.958     3    0.248    1.206
100.00   0.00   2.08  35.99  93.66  90.09     63    0.971     3    0.248    1.219
100.00   0.00   6.06  35.99  93.66  90.09     63    0.971     3    0.248    1.219
100.00   0.00  10.04  35.99  93.66  90.09     63    0.971     3    0.248    1.219

          这时,需要通过分析java程序是临时新增对象更多,还是长期使用的对象更多;如果程序临时新增对象更多,使用完后又销毁了,建议再增大新生代,减少老年代空间;如果认为长期使用对象更多,老年代空间大小不宜减少,则调整新生代中Eden和S0/S1的比例。也可以两种方式都使用。

        本程序为每隔4秒爬取其它网站信息,临时新增对象更多,长期使用的对象并不多,则通过增大新生代空间、减少老年代空间方法来。

        当前JVM配置,分配给新生代的大小为128MB,调整新生代大小为原大小的125%,则为160M。java运行参数为:-server -Xms256m -Xmx256m -Xmn160m -XX:NewRatio=1。查看堆内存情况如下图,可以看到新生代为160MB,老年代为96MB了(正常不建议老年代比新生代小),S0/S1大小为16MB。

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 268435456 (256.0MB)
   NewSize                  = 167772160 (160.0MB)
   MaxNewSize               = 167772160 (160.0MB)
   OldSize                  = 100663296 (96.0MB)
   NewRatio                 = 1
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 150994944 (144.0MB)
   used     = 71386544 (68.07951354980469MB)
   free     = 79608400 (75.92048645019531MB)
   47.27743996514214% used
Eden Space:
   capacity = 134217728 (128.0MB)
   used     = 71386544 (68.07951354980469MB)
   free     = 62831184 (59.92048645019531MB)
   53.18711996078491% used
From Space:
   capacity = 16777216 (16.0MB)
   used     = 0 (0.0MB)
   free     = 16777216 (16.0MB)
   0.0% used
To Space:
   capacity = 16777216 (16.0MB)
   used     = 0 (0.0MB)
   free     = 16777216 (16.0MB)
   0.0% used
tenured generation:
   capacity = 100663296 (96.0MB)
   used     = 13511544 (12.885612487792969MB)
   free     = 87151752 (83.11438751220703MB)
   13.422513008117676% used

         继续监控堆内存增长情况,发现S0/S1出现了91.84,不再全是100,有点效果,但还是有100和过早晋升的情况出现,这时需要不断调整新生代大小和Eden与S0/S1的比例来找到合适的配置。

[root@VM_0_12_centos smartFinancial-manager]# jstat -gcutil 7006 4000
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00  91.84  52.12  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  54.78  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  57.44  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  60.10  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  64.09  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  66.75  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  69.41  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  72.07  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  74.72  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  78.71  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  81.37  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  84.03  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  86.68  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  89.34  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  93.33  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  95.99  13.42  93.66  90.05      5    0.134     2    0.100    0.233
  0.00  91.84  98.65  13.42  93.66  90.05      5    0.134     2    0.100    0.233
100.00   0.00   3.04  17.33  93.39  90.08      6    0.154     2    0.100    0.253
100.00   0.00   6.13  17.33  93.39  90.08      6    0.154     2    0.100    0.253

        经过不断调整和监控,在不加内存的情况下发现一种较好的配置:-server -Xms256m -Xmx256m -Xmn192m -XX:NewRatio=1 -XX:SurvivorRatio=5 -XX:TargetSurvivorRatio=90。TargetSurvivorRatio表示S0/S1的使用率达到90%以上时才将新生代的对象转移到老年代,调大该值可以更高效利用S0/S1的空间(也会有其它风险,自己取舍)。

        最终的堆大小分配情况为:新生代总内存164MB(S0:27 S1:27 Eden:137),老年代内存64MB。程序运行正常,堆内存增长情况如下,内存增长和回收达到了一种相对平衡的状态,大量的临时对象在未使用时,通过YGC就回收了,未再转移到老年代。

[root@VM_0_12_centos ~]# jstat -gcutil 12594 4000
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00  81.08  81.76  22.80  93.89  90.17     11    0.273     2    0.165    0.438
  0.00  81.08  85.62  22.80  93.89  90.17     11    0.273     2    0.165    0.438
  0.00  81.08  87.54  22.80  93.89  90.17     11    0.273     2    0.165    0.438
  0.00  81.08  89.47  22.80  93.89  90.17     11    0.273     2    0.165    0.438
  0.00  81.08  93.33  22.80  93.89  90.17     11    0.273     2    0.165    0.438
  0.00  81.08  95.27  22.80  93.89  90.17     11    0.273     2    0.165    0.438
  0.00  81.08  99.13  22.80  93.89  90.17     11    0.273     2    0.165    0.438
 60.70   0.00   1.76  22.80  94.27  90.26     12    0.294     2    0.165    0.460
 60.70   0.00   3.42  22.80  94.27  90.26     12    0.294     2    0.165    0.460
 60.70   0.00   6.69  22.80  94.27  90.26     12    0.294     2    0.165    0.460
 60.70   0.00   9.96  22.80  94.27  90.26     12    0.294     2    0.165    0.460
 60.70   0.00  11.60  22.80  94.27  90.26     12    0.294     2    0.165    0.460
 60.70   0.00  14.88  22.80  94.27  90.26     12    0.294     2    0.165    0.460
 60.70   0.00  18.15  22.80  94.27  90.26     12    0.294     2    0.165    0.460
 60.70   0.00  19.79  22.80  94.27  90.26     12    0.294     2    0.165    0.460

         调优技巧:通过调整新生代大小,观察FGC(全部回收)和YGC(年轻代垃圾回收)的变化,尽量使FGC增大一次时尽可能多的进行YGC,原理:很多方法中的对象都是临时创建和使用的,用完就回收了,让这些对象的回收尽量发生在YGC中,而不是FGC中,FGC的回收会影响到程序的整体运行(详细了解STW)。