系列文章目录
第一章 如何保证多个线程的顺序执行?
第二章 如何排查线上环境内存使用过大?
第三章 如何模拟FullGC导致CPU满载问题?
文章目录
- 前言
- 排查故障
- 一、构建模拟环境
- 二、Java VisualVM查看
- 在命令行输入,启动VisualVM
- 安装VisualGC插件
- 查看内存分配情况
- 三、模拟频繁Full GC
- 解决思路
- 额外
前言
面试时,经常会问到线上问题排查,诸如内存占用过大,cpu满载,服务器告警等问题,该文就来说一下cpu满载的问题。
造成CPU满载的主要原因有:
- 频繁Full GC
- 代码死循环
- 线程死锁
下面就从第一个方面复现问题,后续会继续出其他情况的模拟
排查故障
一、构建模拟环境
创建springboot项目就省略了,上一章已经创建过,请参考!
配置VM options
-Xmx60m -Xms60m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+PrintGCDateStamps
根据默认的分配比例(1:2)可以得到,年轻代:老年代=20M:40M
年轻代中根据-XX:SurvivorRatio=8,得到eden:s0:s1 = 16M:2M:2M
二、Java VisualVM查看
在命令行输入,启动VisualVM
jvisualvm
可以看到详细的配置信息,包含自己手动添加的VM options
安装VisualGC插件
在最上面找到工具–>插件,安装VisualGC
查看内存分配情况
可以看到内存分配情况,和配置参数是预测的一致,eden:s0:s1 = 16:2:2
young:old = 20:40
三、模拟频繁Full GC
模拟频繁Full GC 导致CPU瞬间满载,代码如下(示例):
@RestController@RequestMapping(value = "/memory")public class MemoryController {@GetMapping("/oom/{n}")public String oom(@PathVariable int n) throws InterruptedException {/*** 创建一个强引用的局部变量,执行完就回收*/List<byte[]> memoryList = new ArrayList<>();/*** 每次请求生成一个n M大小的空间*/memoryList.add(new byte[n * 1024 * 1024]);// 写一个sleep时间,让强引用时间长一点Thread.sleep(1000);return "success";}@GetMapping("/testCpu/{n}")public void testCpu(@PathVariable int n){// 模拟创建多个线程,每个线程创建一个10M的对象for (int i = 0; i < n; i++) {new Thread(()->{try {oom(10);} catch (InterruptedException e) {e.printStackTrace();}}).start();}}}
浏览器频繁调用 http://127.0.0.1:8080/memory/testCpu/100
会出现如下情况
CPU瞬间满载,垃圾回收也达到满值
日志一直在打印FullGC进行垃圾回收
可以发现频繁FullGC会在瞬间导致CPU满载,出现服务器告警或者无法提供服务的情况,如果由于当时用户量大量增加,就会导致服务无法使用的情况。
解决思路
合理调整JVM堆内存比例,防止对象直接进入老年代,导致老年代空间不足,频繁FullGC
额外
手写springboot+orm项目,有用的话还请star一下
https://github.com/AnswerYL/custom