记OutOfMemory异常:GC overhead limit exceeded

问题起源:

项目需要一个统计数量的功能,我在实现这个功能的时候为了代码的结构,在Service中每一个方法都会进行SQL查询,并且count,然而上线后,突然出现了一个问题,CPU占用100%,服务卡住不动了。

这是为啥呢,本地运行是没有问题的。突然想到之前看过服务器如果访问的人数过多,就会导致并发问题。于是我准备在本地进行压力测试。

压力测试工具

这里采用的是WebBench,由于工作环境是mac系统,所以要用命令行操作。

brew install wget # 安装wget,这个是可以从web下载资源的东西
brew install ctags # 依赖安装,是webBench的依赖
wget http://blog.zyan.cc/soft/linux/webbench/webbench-1.5.tar.gz # 下载webBench
tar -zxvf webbench-1.5.tar.gz # 解压文件
cd webbench-1.5 
mkdir -pv /usr/local/man/man1 # 必须
sudo make && sudo make install # sudo 权限因为需要创建文件夹

到此为止,webBench就装好了,以上参考了其他博客。

然后关闭nginx,启动springBoot,准备进行测试。

nginx -s stop  # 关闭nginx
webbench -c 400 --get http://localhost:8080/api/count  # 发送400个并发GET

伴随着大量Full GC,OutOfMemory出现了。

原因

GC overhead limit exceeded

这是一个很极端的情况,内存几乎满了,而GC对他毫无办法,一般来说是由于非常多的零碎的对象,导致的,而我的代码中有很多不停的查询数据库的操作,这些操作是借助于QueryDSL进行而不是原始SQL,每一个方法内部也会有一些对象来存放查询结果,这就导致了这种问题。

解决

整合所有需要查询的方法,合并相同或相似的查询,用stream方法代替查询中的各种条件,以减少中间变量的出现以及查询次数。

操作一通之后,OOM就没了,可以轻松承受之前的并发数。

在刚刚开始修改的时候,我并没有发现有很多的冗余代码可以简化掉,那么问题究竟出在什么地方呢,在这些操作中,有一个很耗时的SQL,单独执行的话无非是慢一点,但是一旦在并发环境下,这就会导致很多在此方法中进行耗时操作的对象的堆积,这些对象还正在被使用,因此GC无法回收他们,也无法得到更多内存,所以就会出现这个问题。

因此,以后的代码中,如果能够通过一个查询就可以解决的,绝对不用多个,避免非常费时间的各类操作(像是SQL什么的)。

猜你喜欢

转载自blog.csdn.net/YunJian01/article/details/84828058