需要监控堆内存使用情况,因此要掌握一些JVM的调优命令工具,参考如下:
调优
我的服务器配置4GB CPU2核 centos
调优前言
新上线一个java服务,或者是RPC或者是WEB站点, 内存的设置该怎么设置呢?设置成多大比较合适,既不浪费内存,又不影响性能呢?
分析:
依据的原则是根据Java Performance里面的推荐公式来进行设置。
通过ps -ef | grep java查看java 进程id
[root@instance-6l2efcjm gclog]# ps -ef | grep java
root 2769 1 1 19:30 pts/0 00:00:42 java -jar -Xloggc:./gclog/gc-service-hosp-1.0.jar-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps /root/jzt/yygh/services/service-hosp-1.0.jar
root 2853 1 0 19:30 pts/0 00:00:32 java -jar -Xloggc:./gclog/gc-service_gateway-1.0.jar-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps /root/jzt/yygh/services/service_gateway-1.0.jar
root 2870 1 0 19:30 pts/0 00:00:20 java -jar -Xloggc:./gclog/gc-hospital-manage-0.0.1-SNAPSHOT.jar-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps /root/jzt/yygh/services/hospital-manage-0.0.1-SNAPSHOT.jar
root 2964 1 0 19:31 pts/0 00:00:28 java -jar -Xloggc:./gclog/gc-service_cmn-1.0.jar-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps /root/jzt/yygh/services/service_cmn-1.0.jar
root 3035 1 0 19:31 pts/0 00:00:27 java -jar -Xloggc:./gclog/gc-service-user-1.0.jar-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps /root/jzt/yygh/services/service-user-1.0.jar
root 3107 1 0 19:31 pts/0 00:00:34 java -jar -Xloggc:./gclog/gc-service-order-1.0.jar-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps /root/jzt/yygh/services/service-order-1.0.jar
root 8169 1576 0 20:37 pts/0 00:00:00 grep --color=auto java
通过 jstat -gc 查看进程service-hosp的堆内存使用情况(单位KB)
-gc:显示与GC相关的堆信息,年轻代、老年代、永久代等的容量、已用空间、GC时间合计等信息
[root@instance-6l2efcjm gclog]# jstat -gc 2769
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
11264.0 5632.0 0.0 5552.2 293376.0 18830.5 85504.0 38596.2 86400.0 81009.2 11648.0 10772.1 23 0.324 3 0.402 0.726
[root@instance-6l2efcjm gclog]# jstat -gc 2870
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
5120.0 8192.0 5104.9 0.0 303104.0 13687.7 45056.0 21593.7 52352.0 50031.9 6784.0 6272.5 14 0.283 2 0.343 0.626
[root@instance-6l2efcjm gclog]# jstat -gc 2853
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
5632.0 2048.0 0.0 1952.0 308224.0 197551.5 45568.0 23339.8 58920.0 55896.0 7720.0 7168.5 19 0.373 2 0.257 0.630
[root@instance-6l2efcjm gclog]# jstat -gc 2964
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
8704.0 9216.0 6318.5 0.0 316416.0 279924.4 60928.0 18385.8 69120.0 65721.9 8960.0 8299.6 20 0.205 3 0.400 0.605
[root@instance-6l2efcjm gclog]# jstat -gc 3035
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
8192.0 8704.0 384.0 0.0 316928.0 307483.9 78848.0 22268.1 59608.0 56694.6 7808.0 7274.5 18 0.162 3 0.361 0.523
[root@instance-6l2efcjm gclog]# jstat -gc 3107
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
1024.0 9728.0 704.0 0.0 289280.0 72757.5 79872.0 31674.6 72704.0 68402.6 9728.0 9020.0 22 0.233 3 0.394 0.627
上述6个java进程中service-hosp进程老年代最大使用空间为37.69MB 四倍则是150MB。 而方法区使用79MB(默认的20.79MB,方法区空间不足)不够用,会造成full gc,因此后面会解决【fullgc问题解决:Full GC (Metadata GC Threshold)】这个问题。
jmap -heap 2769 查看 2769 service-hosp 进程堆内存的详细信息
[root@instance-6l2efcjm gclog]# jmap -heap 2769
Attaching to process ID 2769, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.312-b07
using thread-local object allocation.
Parallel GC with 2 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 1029701632 (982.0MB)
NewSize = 21495808 (20.5MB)
MaxNewSize = 342884352 (327.0MB)
OldSize = 43515904 (41.5MB)
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 = 300417024 (286.5MB)
used = 147572368 (140.73597717285156MB)
free = 152844656 (145.76402282714844MB)
49.12250512141416% used
From Space:
capacity = 5767168 (5.5MB)
used = 5685408 (5.422027587890625MB)
free = 81760 (0.077972412109375MB)
98.58231977982955% used
To Space:
capacity = 11534336 (11.0MB)
used = 0 (0.0MB)
free = 11534336 (11.0MB)
0.0% used
PS Old Generation
capacity = 87556096 (83.5MB)
used = 39522464 (37.691558837890625MB)
free = 48033632 (45.808441162109375MB)
45.1395914226235% used
从上面可以看出,MaxHeapSize默认为物理内存的1/4。由SurvivorRatio=8 可知(E:S:S = 8:1:1)
新生代空间:303MB 老年代空间:83.5MB (这有点离谱,新生代空间比老年代空间还大,这不full gc? 老年代空间不足)
参见参数介绍
-Xms512m 设置JVM促使内存为512m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmx512m ,设置JVM最大可用内存为512M。
-Xmn200m,设置新生代的大小200MB设置年轻代大小为200M。此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是(eden+1 survivor space)不同的。
计算公式有:
年老代大小=-Xmx减去-Xmn
整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。
持久代一般固定大小为64m,所以增大年轻代(-Xmn)后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-XX:SurvivorRatio
用于设置Eden和其中一个Survivor的比值,默认比例为8(Eden):1(一个survivor),这个值也比较重要。
例如:-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6。
-XX:NewRatio参数就是用于调整新生代与老年代的占比的. 和**-Xmn200m**会产生冲突,忽略这个newratio.比如设置了-Xmn1000m,则-XX:NewRatio=4将被忽略
Xmx Xms等堆内存设置
设置Xms初始堆大小,Xmx最大堆大小皆为256M,设置元空间大小128M(避免方法区空间不足,重新分配内存从而产生full gc)
设置NewRatio 老年代:新生代的比例 =2:1防止老年代不足产生full gc.
-Xms256m -Xmx256m -XX:NewRatio=2 -XX:MetaspaceSize=128M -Xloggc:./gclog/gc-$JAR_NAME-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
重新查看service-hosp进程的堆内存信息:
[root@instance-6l2efcjm gclog]# jmap -heap 51165
Attaching to process ID 51165, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.312-b07
using thread-local object allocation.
Parallel GC with 2 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 268435456 (256.0MB)
NewSize = 89128960 (85.0MB)
MaxNewSize = 89128960 (85.0MB)
OldSize = 179306496 (171.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 134217728 (128.0MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 81788928 (78.0MB)
used = 72051336 (68.71350860595703MB)
free = 9737592 (9.286491394042969MB)
88.09424180250902% used
From Space:
capacity = 3670016 (3.5MB)
used = 1324440 (1.2630844116210938MB)
free = 2345576 (2.2369155883789062MB)
36.088126046316965% used
To Space:
capacity = 3670016 (3.5MB)
used = 0 (0.0MB)
free = 3670016 (3.5MB)
0.0% used
PS Old Generation
capacity = 179306496 (171.0MB)
used = 43805776 (41.77644348144531MB)
free = 135500720 (129.2235565185547MB)
24.430668702599597% used
28675 interned Strings occupying 2721384 bytes.
问题
fullgc问题解决:Full GC (Metadata GC Threshold)
微服务在重启或者发布的时候,会有多次的full GC。
首先排查JVM的问题,就要把GC日志打开
exec nohup java -jar -Xloggc:./gclog/gc-$JAR_NAME-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps $JAR_PATH/$JAR_NAME >> $LOG_PATH/nohup.out &
重启Tomcat服务器,发现gc日志如下:
原因描述
通过GC日志可以看到,old区离最大配置还很远,Metaspace区并没有真正释放空间,所以怀疑是Metaspace区不够用了。
以前只认为,Metaspace区是保存在本地内存中,是没有上限的,经查阅资料才发现,原来JDK8中,XX:MaxMetaspaceSize确实是没有上限的,最大容量与机器的内存有关;但是XX:MetaspaceSize是有一个默认值的:21M。问题就出在这里。
最终解决方案
既然问题找到了,那么就设置一个XX:MetaspaceSize的JVM启动参数:-XX:MetaspaceSize=128M.
Metaspace配置说明
从JDK8开始,永久代(PermGen)的概念被废弃掉了,取而代之的是一个称为Metaspace的存储空间。Metaspace使用的是本地内存,而不是堆内存,也就是说在默认情况下Metaspace的大小只与本地内存大小有关。当然你也可以通过以下的几个参数对Metaspace进行控制:
-XX:MetaspaceSize=N
这个参数是初始化的Metaspace大小,该值越大触发Metaspace GC的时机就越晚。随着GC的到来,虚拟机会根据实际情况调控Metaspace的大小,可能增加上线也可能降低。在默认情况下,这个值大小根据不同的平台在12M到20M浮动。使用java -XX:+PrintFlagsInitial命令查看本机的初始化参数,-XX:Metaspacesize为21810376B(大约20.8M)。
-XX:MaxMetaspaceSize=N
这个参数用于限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存,影响到其他程序。在本机上该参数的默认值为4294967295B(大约4096MB)。
-XX:MinMetaspaceFreeRatio=N
当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长Metaspace的大小。在本机该参数的默认值为40,也就是40%。设置该参数可以控制Metaspace的增长的速度,太小的值会导致Metaspace增长的缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长的过快,浪费内存。
-XX:MaxMetasaceFreeRatio=N
当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace的部分空间。在本机该参数的默认值为70,也就是70%。
-XX:MaxMetaspaceExpansion=N
Metaspace增长时的最大幅度。在本机上该参数的默认值为5452592B(大约为5MB)。
-XX:MinMetaspaceExpansion=N
Metaspace增长时的最小幅度。在本机上该参数的默认值为340784B(大约330KB为)。
参考: https://blog.csdn.net/liubenlong007/article/details/78143285
详细了解,可见 这个博客 JDK1.8应用启动出现fullgc问题解决方案:
metaspace在空间不足时,会进行扩容,并逐渐达到设置的MetaspaceSize。Metaspace扩容到 -XX:MetaspaceSize 参数指定的量,就会发生FullGC。如果配置了 -XX:MetaspaceSize,那么触发 FGC 的阈值就是配置的值。
优化响应时间
首先不应该上来就想着优化JVM参数,而是从上至下的顺序,先优化程序,再优化数据库层(查询也是很耗时间的),最后才是JVM层。而我的服务器由于是4GB,放不下数据库层(MySQL、Redis、MongoDB),因此把数据库层放到另一台服务器上,通过网络传输,导致网站的访问时间长达数十秒。
参考https://zhuanlan.zhihu.com/p/479794003