问题现象:springboot服务A刚启动时正常,但运行几个小时后就会接口请求无响应,但服务器网络、磁盘I/O和CPU都没有出现爆满的情况,且A服务日志没有异常报错。
线上SpringBoot假死现象
SpringBoot应用会出现无法访问的情况。具体的表现有3点:表现1. 客户端请求没有任何响应
表现2. 请求时, 没有任何日志输出
表现3. SpringBoot进程是存活的
排查过程:
1.由于服务器网络、磁盘I/O和CPU都没有出现爆满的情况,且A服务日志没有异常报错,无法直接通过看日志分析问题。所以使用jprofile查看服务运行状况。
通过观察jvm堆内存,GC活动,CPU负载以及线程,死锁等一下子没发现异常,但是通过长时间观察发现线程数会不断增加,由初始的几十个总线程,增长到两三百个线程,且大部分是在进行网络I/O的线程。
2.通过观察XNIO-1线程不断增加,便继续观察后面不断新增的线程的调用树
3.通过线程调用树定位到长时间占用资源的代码片段,发现由于在通过http请求调用服务B时,连接建立了但无响应,导致线程无法释放。
4.于是查看服务B,发现服务B日志报错java.lang.OutOfMemoryError: Java heap space
内存溢出了!
5.进入docker容器中,
使用jps查看进程id,
再使用jmap -histo pid查看堆内存情况
jps
jmap -histo pid
执行jmap -histo pid可以打印出当前堆中所有每个类的实例数量和内存占用,如下,class
name是每个类的类名([B是byte类型,[C是char类型,[I是int类型),bytes是这个类的所有示例占用内存大小,instances是这个类的实例数量。
6.通过观察堆内存情况,以及结合其他日志分析,定位到在使用netty客户端时,创建的EventLoopGroup没有及时释放掉导致资源不断堆积。完善对应代码后重新部署服务即解决问题。
over!