最近公司生产环境偶尔会出现CPU暴涨导致大量请求失效。但是从打印的日志又没发现有什么异常信息,导致排查问题无从下手。
下午收到反馈的时候,CPU还在高位,看到现象重现,赶紧对其进行分析。
首先通过jps -l 命令查看当前JAVA项目进程ID。然后根据jstack -l pid > stack.txt以及 jstat -gc pid > gc.txt命令导出线程和GC相关信息。然后再根据jmap -dump:format=b,file=heap.dump pid 命令导出相关的堆栈信息,这个命令导出的文件比较大,会耗费较多的时间。
导出相关日志后,从stack.txt日志上看,没有死锁,但是存在GC线程,暂未看出具体问题。然后从gc.txt上看到Eden区和老年区内存使用占比非常高,而且FULL GC次数很多,怀疑是FULL GC 导致CPU使用飙升。
公司网络较慢,经过20分钟,终于将堆栈日志heap.dump从云平台上拉取出来。通过visualvm软件打开,可看到当前堆栈中存在大量HSSFCell对象,这个对象是导出生成EXCEL的,由此怀疑是导出大数据量数据,导致生成了大量的临时对象,将新生代和老年代空间都占满了,从而引起FULL GC,而FULL GC又无法释放出足够内存,导致多次FULL GC 从而导致CPU持续飙升导致大量请求处理失败。
分析到问题后,回到线程日志stack.txt查找是否存在导出线程,发现存在一个大数据量导出。 定位问题后,对其分析可知,是在导出的时候生成了大量的临时对象,导致Eden区内存被占满引发 YGC,但是无法释放足够空间,进而引发多次FULL GC。
解决办法从硬件上考虑,结合平时CPU的使用率没有很高,考虑扩大Eden区的内存,也就是调整新老年代的占比。但是后面又看到平台上仍存在足够内存,便采取扩大内存。
程序上考虑认为生成EXCEL文件确实需要生成比较多的对象,占用内存也较多,所以当导出大批量数据时考虑生成CSV文件。