1. 频繁 Full GC 怎么排查?
这种问题最好的办法就是结合有具体的例子举例分析,如果没有就说一般的分析步骤。发生FGC
有可能是内存分配不合理,比如Eden
区太小,导致对象频繁进入老年代,这时候通过启动参数配置就能看出来,另外有可能就是存在内存泄露,可以通过以下的步骤进行排查:
1.1 使用jstat -gcutil
命令或者查看gc.log
日志,查看内存回收情况。
- S0 S1 分别代表两个 Survivor 区占比。
- E 代表 Eden 区占比,图中可以看到使用78%。
- O 代表老年代,M 代表元空间,YGC 发生54次,YGCT 代表 YGC 累计耗时,GCT 代表 GC 累计耗时。
- [GC [FGC 开头代表垃圾回收的类型。
- PSYoungGen: 6130K->6130K(9216K)] 12274K->14330K(19456K), 0.0034895 secs代表YGC前后内存使用情况。
- Times: user=0.02 sys=0.00, real=0.00 secs,user 表示用户态消耗的 CPU 时间,sys 表示内核态消耗的 CPU 时间,real 表示各种时钟的等待时间。
这两张图只是举例并没有关联关系,比如你从图里面看能到是否进行FGC,FGC的时间花费多长,GC后老年代,年轻代内存是否有减少,得到一些初步的情况来做出判断。
1.2 dump出内存文件再具体分析
比如通过jmap
命令jmap -dump:format=b,file=dumpfile pid
,导出之后再通过Eclipse Memory Analyzer等工具进行分析,定位到代码,修复。
2. CPU飙高,同时FGC怎么办?
这里的处理和上面的方法类似:
- 找到当前进程的pid,top -p pid -H 查看资源占用,找到线程
- printf “%x\n” pid,把线程pid转为16进制,比如0x32d
- jstack pid|grep -A 10 0x32d查看线程的堆栈日志,还找不到问题继续
-
3. JVM 调优有什么经验吗?
要明白一点,所有的调优的目的都是为了用更小的硬件成本达到更高的吞吐,JVM 的调优也是一样,通过对垃圾收集器和内存分配的调优达到性能的最佳。
3.1 简单的参数含义
首先,需要知道几个主要的参数含义。
-Xms设置初始堆的大小,-Xmx设置最大堆的大小
- -XX:NewSize年轻代大小,-XX:MaxNewSize年轻代最大值,-Xmn则是相当于同时配置-XX:NewSize和-XX:MaxNewSize为一样的值
- -XX:NewRatio设置年轻代和年老代的比值,如果为3,表示年轻代与老年代比值为1:3,默认值为2
- -XX:SurvivorRatio年轻代和两个Survivor的比值,默认8,代表比值为8:1:1
- -XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。
- -XX:MaxTenuringThreshold设定对象在Survivor复制的最大年龄阈值,超过阈值转移到老年代
-XX:MaxDirectMemorySize当Direct ByteBuffer分配的堆外内存到达指定大小后,即触发Full GC
3.2 调优
为了打印日志方便排查问题最好开启GC日志,开启GC日志对性能影响微乎其微,但是能帮助我们快速排查定位问题。-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:gc.log
- 一般设置-Xms=-Xmx,这样可以获得固定大小的堆内存,减少GC的次数和耗时,可以使得堆相对稳定
- -XX:+HeapDumpOnOutOfMemoryError让JVM在发生内存溢出的时候自动生成内存快照,方便排查问题
- -Xmn设置新生代的大小,太小会增加YGC,太大会减小老年代大小,一般设置为整个堆的1/4到1/3
- 设置-XX:+DisableExplicitGC禁止系统System.gc(),防止手动误触发FGC造成问题