参数 | 备注 | 默认 |
-XX:NewRatio=n | 老年代比新生代的大小比值 | 2 |
-XX:SurvivorRatio=n | 伊甸园:survivor大小比值 | 8 |
-Xms | 最小堆内存 | |
-Xmx | 最大堆内存 | |
-XX:NewSize=n | 新生代初始内存大小 | |
-XX:MaxNewSize=n | 新生代最大内存 | |
1,分析GC日志
1.设置启动参数
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseSerialGC -Xmx50m -Xloggc:C:/Users/xcdpj/Desktop/gcLog/gc.log
Java HotSpot(TM) 64-Bit Server VM (25.40-b25) for windows-amd64 JRE (1.8.0_40-b26), built on Mar 7 2015 13:51:59 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 16632032k(7656916k free), swap 23447776k(8228024k free)
CommandLine flags: -XX:InitialHeapSize=52428800 -XX:MaxHeapSize=52428800 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC
2021-09-03T20:02:32.864+0800: 3.287: [GC (Allocation Failure) 3.288: [DefNew: 13696K->1664K(15360K), 0.0062265 secs] 13696K->2396K(49536K), 0.0076791 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
2021-09-03T20:02:44.597+0800: 15.011: [GC (Allocation Failure) 15.012: [DefNew: 15360K->1663K(15360K), 0.0115922 secs] 16092K->4609K(49536K), 0.0121714 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
2021-09-03T20:03:21.394+0800: 51.809: [GC (Allocation Failure) 51.809: [DefNew: 15359K->1664K(15360K), 0.0347591 secs] 18305K->11560K(49536K), 0.0348934 secs] [Times: user=0.05 sys=0.00, real=0.03 secs]
2021-09-03T20:04:01.894+0800: 92.309: [GC (Allocation Failure) 92.309: [DefNew: 15360K->1663K(15360K), 0.0303953 secs] 25256K->19288K(49536K), 0.0305053 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
2021-09-03T20:04:41.830+0800: 132.245: [GC (Allocation Failure) 132.245: [DefNew: 15359K->1664K(15360K), 0.0275518 secs] 32984K->26895K(49536K), 0.0276716 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
2021-09-03T20:05:23.134+0800: 173.549: [GC (Allocation Failure) 173.549: [DefNew: 15360K->15360K(15360K), 0.0000308 secs]173.549: [Tenured: 25231K->34175K(34176K), 0.0878445 secs] 40591K->34520K(49536K), [Metaspace: 4722K->4722K(1056768K)], 0.0880149 secs] [Times: user=0.08 sys=0.00, real=0.09 secs]
2021-09-03T20:06:08.505+0800: 218.920: [Full GC (Allocation Failure) 218.920: [Tenured: 34176K->34176K(34176K), 0.1141824 secs] 49535K->43895K(49536K), [Metaspace: 4718K->4718K(1056768K)], 0.1143146 secs] [Times: user=0.13 sys=0.00, real=0.11 secs]
2021-09-03T20:06:26.363+0800: 236.778: [Full GC (Allocation Failure) 236.778: [Tenured: 34176K->34176K(34176K), 0.1134000 secs] 49535K->47184K(49536K), [Metaspace: 4718K->4718K(1056768K)], 0.1135354 secs] [Times: user=0.13 sys=0.00, real=0.11 secs]
2021-09-03T20:06:34.044+0800: 244.459: [Full GC (Allocation Failure) 244.459: [Tenured: 34176K->34175K(34176K), 0.1301209 secs] 49535K->47463K(49536K), [Metaspace: 4718K->4718K(1056768K)], 0.1302669 secs] [Times: user=0.13 sys=0.00, real=0.13 secs]
比较懒直接截图介绍
2021-09-03T20:02:32.864+0800: 3.287: [GC (Allocation Failure) 3.288: [DefNew: 13696K->1664K(15360K), 0.0062265 secs] 13696K->2396K(49536K), 0.0076791 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
2021-09-03T20:06:34.044+0800: 244.459: [Full GC (Allocation Failure) 244.459: [Tenured: 34176K->34175K(34176K), 0.1301209 secs] 49535K->47463K(49536K), [Metaspace: 4718K->4718K(1056768K)], 0.1302669 secs] [Times: user=0.13 sys=0.00, real=0.13 secs]
在线工具
/**
* 默认参数 https://chriswhocodes.com/hotspot_options_openjdk8.html
* 在线建议jvm参数设置 https://opts.console.heapdump.cn/
* java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseSerialGC -Xmx50m -Xloggc:C:/Users/xcdpj/Desktop/gcLog/gc.log
*/
2.常见问题定位
1.cpu过高定位分析
top + jstack /或者 使用JMC(服务器开启jmx)
1.top找出cpu占用最高的进程,如下图 pid 为36032
2.top -Hp 36032 得到进程里面线程占用cpu最高的线程 ==> 36044 thread-0
3.36044转化为16进制: printf %x 36044 ==> 8ccc (jstack dump下来的文件是十六进制的)
4.dump cpu过高的进程把结果输出到log.txt。 jstack 36032 > log.txt
--查询8ccc 30行
5.cat log.txt | grep -A 30 8ccc
场景 while 无限循环(睡眠一会)) 、频繁gc(合理设置堆大小)、频繁创建新对象(使用单例)、序列化和反序列化(使用类库不合理)、正则表达式(改写正则表达式)
找打具体的代码行数
2 内存溢出
1 堆内存溢出(内存泄漏、非内存泄漏)
获取dump文件,使用 visvalVM 分析堆dump文件,定位到行数
dump文件设置
-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\dumpLog
实际项目中,堆内存溢出可能导致线程的奔溃,可能进程直接挂掉了,没有日志。
使用工具排查,如果不是堆内存溢(比如分页情况每页数量导致数据太大,后端逻辑严谨处理)
2栈内存溢出
3方法区内存溢出
4直接内存溢出
5代码缓存区满
3 项目越来越慢的问题解决?
stw 过长(垃圾收集过于频繁)、数据库数据越来越多(sql优化,索引。分表、分区)、codeCache满了、线程争抢、进程争抢
4如何选择垃圾收集器?
JDK8中 有Serial收集器、Parallel收集器、CMS收集器、G1收集器这么几种收集器,需要根据实际硬件配置和业务需求进行选择调优。
4.1 Serial收集器
场景: 桌面应用,内存占用也就100MB。
Serial收集器是使用单线程处理所有的垃圾收集工作的,因为没有多线程的额外开销,相对来说也是比较有效的,合单核CPU环境
JVM参数-XX:+UseSerialGC
4.2Parallel收集器(jdk8 默认)
场景:要求高吞吐量的应用,使用较大内存并且有多核CPU
Parallel收集器是类似于Serial收集器的分代收集器,主要区别是在垃圾回收的时候使用了多个线程进行加速垃圾的收集。启用Parallel收集器后默认情况下,Minor垃圾收集(针对年轻代的垃圾收集)和Major垃圾收集(针对老年代的垃圾收集)都是并行执行的,可以进一步减少垃圾收集的开销。
JVM参数-XX:+UseParallelGC
JVM参数指定最大垃圾收集暂停时间、吞吐量(用户代码运行时间/(用户代码运行时间+垃圾收集运行时间))和堆占用空间的目标值
- -XX:MaxGCPauseMillis:最大垃圾收集暂停时间,单位为毫秒,如:-XX:MaxGCPauseMillis=200,表示垃圾收集暂停时间最大为200毫秒。默认情况下,没有指定最大垃圾收集暂停时间。如果指定了暂停时间目标,则会调整堆大小与垃圾收集相关的其他参数,使垃圾收集的暂停时间短于指定值。这些调整可能导致降低应用的整体吞吐量,也有可能无法始终满足所指定的最大垃圾收集暂停时间目标。
- -XX:GCTimeRatio:吞吐量大小,如:-XX:GCTimeRatio=19,表示将垃圾收集运行时间的目标设定为应用总运行时间(用户代码运行时间+垃圾收集运行时间)的1/(1+19),即5%。默认值为99,垃圾收集的目标时间占应用总运行时间的1/(1+99),即1%。
- -Xmx:堆占用的最大占用空间,如:-Xmx1G,表示堆占用的最大占用空间为1GB。另外,Parallel收集器还有一个隐含的目标:只要满足其他目标的同时,把堆占用内存的大小最小化。
这三个目标是有优先级的:
- 高优先级:最大垃圾收集暂停时间
- 中优先级:吞吐量目标
- 低优先级:最小堆占用内存目标
4.3CMS收集器
场景:同样是使用较大内存并且有多核CPU,但是要求垃圾收集暂停时间要尽可能短的Web应用
CMS(Concurrent Mark Sweep)收集器是为那些要求垃圾收集暂停时间尽可能短,并且可以和垃圾收集器共享CPU资源的应用设计的。具有相对较大的内存使用并有多核CPU的应用,往往会更适合CMS收集器的使用。可以使用JVM参数-XX:+UseConcMarkSweepGC启用CMS收集器,启用后同时作用于Minor垃圾收集(针对年轻代的垃圾收集)和Major垃圾收集(针对老年代的垃圾收集)。
CMS收集器的垃圾收集过程分为以下四个步骤:
CMS收集器尝试通过使用单独的垃圾收集器线程在执行用户线程的同时并跟踪可访问对象,来减少由于Major垃圾收集而导致的暂停时间。在每个Major垃圾收集周期中,CMS收集器会在收集开始时暂停所有用户线程一小段时间,然后在收集的中期再次暂停。第二个暂停往往是两个暂停中较长的一个,在两个暂停之间都使用多个线程并行做收集工作的。所以,CMS收集器的垃圾收集过程分为以下四个步骤:
- 初始标记(CMS initial mark):这个步骤会暂停所有用户线程,但耗时非常短,标记GC Root直接关联的对象。
- 并发标记(CMS concurrent mark):这个步骤耗时较长,但用户线程可同时运行,标记至GC Root有可达路径的对象。
- 重新标记(CMS remark):这个步骤会暂停所有用户线程,但耗时比较短。由于步骤2用户线程同步运行,所以要修正在步骤二中用户线程同步运行产生对象标记的变动。
- 并发清除(CMS concurrent sweep):这个步骤耗时较长,但用户线程可同时运行
4.4 G1收集器
场景:如果堆中有超过50%的活跃对象,分配对象和对象升代的频率较高,垃圾收集停顿时间大于0.5秒
- G1 能充分利用多 CPU 、多核环境硬件优势,尽量缩短 STW
- G1 整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片
- G1 的停顿可预测,能明确指定在一个时间段内,消耗在垃圾收集上的时间不能超过多长时间
- G1 跟踪各个 Region 里面垃圾堆的价值大小,在后台维护一个优先列表,每次根据允许的时间来回收价值最大的区域,从而保证在有限时间内的高效收集
对于jdk8 G1 or CMS?
如果内存小于等于6g,建议使用cms,如果内存大于6g,考虑使用G1
原因:G1存在空间浪费,因为G1回收对象的时候使用复制算法把活跃的对象复制到空闲的Region里面去。CMS收集器在老年代空间使用68%开始回收垃圾
目录