上周运维反馈线上程序出现了OOM,程序日志中的输出为

Exception in thread "http-nio-8080-exec-1027" java.lang.OutOfMemoryError: Java heap space
Exception in thread "http-nio-8080-exec-1031" java.lang.OutOfMemoryError: Java heap space

看线程名称应该是tomcat的nio工作线程,线程在处理程序的时候因为无法在堆中分配更多内存出现了OOM,幸好JVM启动参数配置了-XX:+HeapDumpOnOutOfMemoryError,使用MAT打开拿到的hprof文件进行分析。

第一步就是打开Histogram看看占用内存最大的是什么对象:



可以看到byte数组占用了接近JVM配置的最大堆的大小也就是8GB,显然这是OOM的原因。

第二步看一下究竟是哪些byte数组,数组是啥内容:



可以看到很明显这和HTTP请求相关,一个数组大概是10M的大小。

第三步通过查看GC根查看谁持有了数组的引用:



这符合之前的猜测,是tomcat的线程在处理过程中分配了10M的buffer在堆上。至此,马上可以想到一定是什么参数设置的不合理导致了这种情况,一般而言tomcat不可能为每一个请求分配如此大的buffer。

第四步就是检查代码里是否有tomcat或服务器相关配置,看到有这么一个配置:

max-http-header-size: 10000000

至此,基本已经确定了八九不离十就是这个不合理的最大http请求头参数导致的问题。

到这里还有3个疑问:

  1. 即使一个请求分配10M内存,堆有8GB,难道当时有这么多并发吗?800个tomcat线程?
  2. 参数只是设置了最大请求头10M,为什么tomcat就会一次性分配这么大的buffer呢?
  3. 为什么会有如此多的tomcat线程?感觉程序没这么多并发。

先来看问题1,这个可以通过MAT在dump中继续寻找答案。

可以打开线程视图,搜索一下tomcat的工作线程,发现线程数量的确很多有401个,但是也只是800的一半:



再回到那些大数组的清单,按照堆分配大小排序,往下看:



可以发现除了有10008192字节的数组还有10000000字节的数组,查看引用路径可以看到这个正好是10M的数组是output buffer,区别于之前看到的input buffer:



好吧,这就对了,一个线程分配了输入输出两个buffer,占用20M内存,一共401个线程,占用8GB,所以OOM了。

还引申出一个问题为啥有这么多工作线程,

再来看看问题2,这就需要来找一下源码了,首先max-http-header-size是springboot定义的参数,查看springboot代码可以看到这个参数对于tomcat设置的是MaxHttpHeaderSize:



然后来看看tomcat源码:



进一步看一下input buffer:



buffer大小是MaxHttpHeaderSize+ReadBuffer大小,后者默认是8192字节:

   <attribute name="socket.appReadBufSize" required="false">
<p>(int)Each connection that is opened up in Tomcat get associated with
a read ByteBuffer. This attribute controls the size of this buffer. By
default this read buffer is sized at <code>8192</code> bytes. For lower
concurrency, you can increase this to buffer more data. For an extreme
amount of keep alive connections, decrease this number or increase your
heap size.</p>
</attribute>

这也就是为什么之前看到大量的buffer是10008192字节的。显然还有一批内容是空的10000000字节的buffer应该是output buffer,来看看源码:



嗯这是一个header buffer,所以正好是10000000字节。

至于问题3,显然我们的应用程序是配置过最大线程的(查看配置后发现的确,我们配置为了2000,好吧有点大),否则也不会有401个工作线程(默认150),如果当时并发并不大的话就一种可能,请求很慢,虽然并发不大,但是因为请求执行的慢就需要更多线程,比如TPS是100,但是平均RT是4s的话,就是400线程了。这个问题的答案还是可以通过MAT去找,随便看几个线程可以发现很多线程都在等待一个外部服务的返回,这说明外部服务比较慢,去搜索当时的程序日志可以发现有很多"feign.RetryableException: Read timed out executing的日志"。。。。追杀下游去!慢点,我们的feign的timeout也需要再去设置一下,别被外部服务拖死了。

记一次OOM问题排查过程的更多相关文章

  1. 记一次oom问题排查

    大家好,我是大彬~ 今天给大家分享最近出现的OOM问题. 上周五早上,测试同学反馈测试环境的子系统服务一直超时,请求没有响应. 收到这个问题之后,我有点纳闷,最近这个系统也没有改动代码逻辑,怎么会突然 ...

  2. 解Bug之路-记一次中间件导致的慢SQL排查过程

    解Bug之路-记一次中间件导致的慢SQL排查过程 前言 最近发现线上出现一个奇葩的问题,这问题让笔者定位了好长时间,期间排查问题的过程还是挺有意思的,正好博客也好久不更新了,就以此为素材写出了本篇文章 ...

  3. 解Bug之路-记一次存储故障的排查过程

    解Bug之路-记一次存储故障的排查过程 高可用真是一丝细节都不得马虎.平时跑的好好的系统,在相应硬件出现故障时就会引发出潜在的Bug.偏偏这些故障在应用层的表现稀奇古怪,很难让人联想到是硬件出了问题, ...

  4. 记一次生产环境Nginx日志骤增的问题排查过程

    摘要:众所周知,Nginx是目前最流行的Web Server之一,也广泛应用于负载均衡.反向代理等服务,但使用过程中可能因为对Nginx工作原理.变量含义理解错误,或是参数配置不当导致Nginx工作异 ...

  5. JAVA本地环境启动OOM问题排查

    1.问题描述 每次启动信息报错如下: 2.排查过程 2.1起初怀疑是堆内存不够: 将本地队内存调整由-Xms512M,-Xmx1024M,改成与测试环境相同1536M,还是失败 如上图报错中有noti ...

  6. 记一次NoHttpResponseException问题排查

    上传文件程序会有一定的概率提示错误,错误率大概在1%以下,错误信息是:org.apache.http.NoHttpResponseException , s3-us-west-1.amazonaws. ...

  7. 一次kibana服务失败的排查过程

    公司在kubernetes集群上稳定运行数月的kibana服务于昨天下午突然无法正常提供服务,访问kibana地址后提示如下信息: 排查过程: 看到提示后,第一反应肯定是检查elasticsearch ...

  8. 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查)

    原贴如下 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查) 虽然我用的是PHPstudy部署的dedecms,还是一样栽倒这个坑里了. 总结经验:本地测试使用8000~9000的端口比较安全.

  9. 记一次数据库调优过程(IIS发过来SQLSERVER 的FETCH API_CURSOR语句是神马?)

    记一次数据库调优过程(IIS发过来SQLSERVER 的FETCH API_CURSOR语句是神马?) 前几天帮客户优化一个数据库,那个数据库的大小是6G 这麽小的数据库按道理不会有太大的性能问题的, ...

随机推荐

  1. Qt之事件处理机制

    思维导读 一.事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下 ...

  2. 使用.NET中的XML注释(一) -- XML注释标签讲解

    一.摘要 .Net允许开发人员在源代码中插入XML注释,这在多人协作开发的时候显得特别有用. C#解析器可以把代码文件中的这些XML标记提取出来,并作进一步的处理为外部文档. 这篇文章将展示如何使用这 ...

  3. oracle如何insert into 多个values

    稍微熟悉Oracle的都知道,如果我们想一条SQL语句向表中插入多个值的话,如果如下语句 INSERT INTO 某表 VALUES(各个值),VALUES(各个值),.....; 这样会报错的,因为 ...

  4. JvisualVm添加远程监控

    一.Weblogic远程监控 1.首先需要在远程的weblogic的域下面,找到/bin/ setDomainEnv.sh ,需要在此文件下加入如下内容: -Dcom.sun.management.j ...

  5. (转载)Windows: "net use" command introduction

    1)建立空连接: net use ""IP"ipc$ "" /user:"" (一定要注意:这一行命令中包含了3个空格) 2)建立 ...

  6. ipad与iphone的屏幕分辨率

    1.ipad分辨率,iphone 6 iPhone设备      尺寸 分辨率                   点iPhone 3和3s  3.5英寸    (320×480)         3 ...

  7. CSS动画复习

    一.css动画相关的几个属性 属性 含义 理解 transform 一种CSS属性.用于修改CSS视觉格式模型的坐标空间.使用它,元素可以被移动(translate).旋转(rotate).缩放(sc ...

  8. boost_1.61.0编译安装

    1.下载源码boost_1_61_0.zip 2.进入目录 C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shor ...

  9. sourcetree 安装与操作

    sourcetree操作 http://www.jianshu.com/p/be9f0484af9d SourceTree安装教程和GitLab配置详解 http://www.cnblogs.com/ ...

  10. C# 获取计算机的硬件信息

    /// <summary> /// 获得CPU编号 /// </summary> /// <returns></returns> public stri ...