jvm笔记|nohup java -jar 启动springboot项目时JVM参数调优

背景: 【jvm笔记|nohup java -jar 启动springboot项目时JVM参数调优】java version "1.8.0_11"
项目用springboot写的,以往启动都没有设置JVM参数,都是默认的。
今天遇到频繁FGC的情况,导致服务器CPU占用超级高,而且FGC并不能有效的回收对象内存。
1.ps查询对应进程:

[root@10-9-166-14 server]# ps aux|grep addition root22670 43.55.8 4734040 944636 ?Sl21:3753:18 java -jar -server -Xms1g -Xmx1g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 -XX:G1MaxNewSizePercent=30 -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStampsaddition031601.war root302820.00.0 103256972 pts/2S+23:400:00 grep addition

2.jstat -gcutil (查看GC汇总信息),看下对应的堆内存各部分的使用量:
[root@ logs]# jstat -gcutil 15984 S0S1EOMCCSYGCYGCTFGCFGCTGCT 0.000.00 100.00 100.0055.4879.7072824 1046.4971339 3559.981 4606.479

显示列名 具体描述
S0 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
E 年轻代中Eden(伊甸园)已使用的占当前容量百分比
O old代已使用的占当前容量百分比
M 元数据空间使用比例
CCS 压缩使用比例
YGC 从应用程序启动到采样时年轻代中gc次数
YGCT 从应用程序启动到采样时年轻代中gc所用时间(s)
FGC 从应用程序启动到采样时old代(全gc)gc次数
FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT 从应用程序启动到采样时gc用的总时间(s)
可以看到
年轻代Eden区和老年代的使用容量都是100%
补充个知识点:
大多数情况下,对象在新生代Eden区分配。(大对象会直接在老年代分配,使用参数进行设置)当Eden区分配足够的空间进行分配时,就会触发MinorGc(新生代GC)。将超过GC年龄的对象移动到老年代中。
大致就能断定是对象Eden区分配内存时容量不够触发了GC,而导致的GC停顿。但是由于新生代和老年代的使用都是100%而对象又全都在被使用。所以无法回收内存。导致一直GC,一直停顿。。。
3.jinfo查看进行相关JVM信息
VM Flags: Non-default VM flags: -XX:ConcGCThreads=1 -XX:+DisableExplicitGC -XX:G1HeapRegionSize=1048576 -XX:G1MaxNewSizePercent=30 -XX:G1NewSizePercent=20 -XX:InitialHeapSize=1073741824 -XX:MarkStackSize=4194304 -XX:MaxGCPauseMillis=200 -XX:MaxHeapSize=1073741824 -XX:MaxNewSize=321912832 -XX:MinHeapDeltaBytes=1048576 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC Command line:-Xms1g -Xmx1g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 -XX:G1MaxNewSizePercent=30 -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps

可以看到看到默认的VM参数中 新生代和老生代的内存都非常的小,导致了GC的频繁触发。而老生代过小则导致了还在使用的大对象占用所有老生代内存,而不能被回收.
所以,解决方式就是启动时指定新生代和老生代的大小。
增大内存,不让GC频繁触发,不让少部分的大对象就占用所有的老生代内存。
4.nohup启动时指定JVM参数
nohup java -jar -server -Xms1g -Xmx1g -XX:+UseG1GC-XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 -XX:G1MaxNewSizePercent=30 -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/data/www/$project_name/online/logs/gc_log_8000.log***.war/jar > javaxxx.log &

-Xms1g: 最小内存最小
-Xmx1g: 最大内存最大
建议设置-Xms与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存.
-XX:+UseG1GC
指定使用G1(GC收集器)
-XX:MaxGCPauseMillis=200
最长的GC暂停时间,如果时间过长,会相应调整空间的大小(单位是毫秒)
-XX:+UnlockExperimentalJVMOptions
有些时候当设置一个特定的JVM参数时,JVM会在输出“Unrecognized VM option”后终止。如果发生了这种情况,你应该首先检查你是否输错了参数。然而,如果参数输入是正确的,并且JVM并不识别,或许需要设置-XX:+UnlockExperimentalVMOptions 来解锁参数
-XX:G1NewSizePercent=20
设置要用作年轻代大小最小值的堆百分比。默认值是 Java 堆的 5%
-XX:G1MaxNewSizePercent=30
设置要用作年轻代大小最大值的堆大小百分比。默认值是 Java 堆的 60%。
-XX:+DisableExplicitGC
关闭系统调用GC功能 System.gc() 默认会触发一次Full Gc
-XX:+PrintGC
打印GC
-XX:+PrintGCDetails
打印GC详细信息
-XX:+PrintGCTimeStamps
打印GC时间戳
-Xloggc:/data/www/$project_name/online/logs/gc_log_8000.log
GC日志路径
5.启动后使用jstat -gcutil <毫秒数> 查看进程GC过程 jvm笔记|nohup java -jar 启动springboot项目时JVM参数调优
文章图片

在新生代和老年代都调大之后,发现老年代的使用占比小了很多,之前一启动就占了70%左右
通过观察GC次数,和每一次GC之后新生代和老年代的内存使用率就能大概看出内存回收的效率。如果还是出现GC无法回收的话,就需要dump 快照进行分析了。。。
6.一份更详细的启动参数配置
nohup java -jar -server -Xms1G -Xmx1G -XX:+UseG1GC-XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 -XX:G1MaxNewSizePercent=30 -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/www/$project_name/online/logs/oom-error_8000.log -Xloggc:/data/www/$project_name/online/logs/gc_log_8000.log***.jar> ***.log &

比上面用的增加了在out of memory的情况下的内存dump,以及输出的out是哪里,这些如果不需要可以不用加。用上面的就行
-XX:+HeapDumpOnOutOfMemoryError
在out of memory的情况下的内存dump
-XX:HeapDumpPath=/data/www/$project_name/online/logs/oom-error_8000.log
指定out of memory错误输出地址
7.特别说明 -XX:MaxGCPauseMillis=200
最长的GC暂停时间,如果时间过长,会相应调整空间的大小(单位是毫秒)
这个参数在使用的使用要特别注意。如果系统的大对象比较多的话,不要设置这个。
设置了之后,会导致GC频繁系统卡顿。
我之前就设置了。。。最近发现系统变卡了。
比如1G内存,正常到了1G才开始回收,可能回收一次需要1s,但是由于设置了XX:MaxGCPauseMillis=200。可能导致使用到200MB或者更少的时候就开始触发回收,导致设置的大内存压根没有用到。
如果有帮助,点个赞啊亲!

    推荐阅读